diff --git a/CMakeLists.txt b/CMakeLists.txt index 175480e..b4477e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRC_FILES source/common/sys.cpp source/common/lang.cpp source/common/error.cpp + source/common/io.cpp # Controls source/controls/hid.cpp # Maths @@ -40,6 +41,7 @@ set(SRC_FILES source/maths/img_convert.cpp # Graphics source/graphics/texture.cpp + source/graphics/spritesheet.cpp source/graphics/li7_shader.cpp source/graphics/lithium.cpp # Overlays @@ -51,6 +53,7 @@ set(SRC_FILES source/tools/gamepad_icons.cpp # UI7 source/ui7/drawlist.cpp + source/ui7/menu.cpp source/ui7/theme.cpp source/ui7/ui7.cpp # External diff --git a/include/pd.hpp b/include/pd.hpp index db68839..d5d2f2b 100644 --- a/include/pd.hpp +++ b/include/pd.hpp @@ -32,6 +32,7 @@ SOFTWARE. #include // Graphics #include +#include #include // Maths #include @@ -46,4 +47,11 @@ SOFTWARE. // UI7 #include +/// Setup these as non Namespaced access by default +#ifndef PD_MATH_NAMESPACED +using vec2 = PD::vec2; +using vec3 = PD::vec3; +using vec4 = PD::vec4; +#endif + // namespace Palladium = PD; \ No newline at end of file diff --git a/include/pd/common/app.hpp b/include/pd/common/app.hpp index f883999..f50f841 100644 --- a/include/pd/common/app.hpp +++ b/include/pd/common/app.hpp @@ -32,9 +32,14 @@ SOFTWARE. namespace PD { /// @brief Template Class for User Application -class App : public SmartCtor { +class App { public: - App() = default; + App() { + if (too) { + Error("Only one App can be created at the same time!"); + } + too++; + } ~App() = default; /// @brief Templete function where the user can Init his stuff @@ -50,8 +55,8 @@ class App : public SmartCtor { /// @brief Function to run the App /// (int main() { - /// auto app = PD::New(); - /// app->Run(); + /// UserApp app; + /// app.Run(); /// return 0; /// }) void Run(); @@ -71,5 +76,8 @@ class App : public SmartCtor { u64 last_time; float app_time; float fps; + + /// The Only One + static int too; }; } // namespace PD \ No newline at end of file diff --git a/include/pd/common/io.hpp b/include/pd/common/io.hpp new file mode 100644 index 0000000..3cdb208 --- /dev/null +++ b/include/pd/common/io.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace PD { +namespace IO { +std::vector LoadFile2Mem(const std::string& path); +u32 HashMemory(const std::vector& data); +} // namespace IO +} // namespace PD \ No newline at end of file diff --git a/include/pd/common/sys.hpp b/include/pd/common/sys.hpp index 59c5956..15db41f 100644 --- a/include/pd/common/sys.hpp +++ b/include/pd/common/sys.hpp @@ -32,6 +32,7 @@ using TraceMap = std::map; u64 GetTime(); u64 GetNanoTime(); TT::Res::Ref& GetTraceRef(const std::string& id); +bool TraceExist(const std::string& id); TraceMap& GetTraceMap(); } // namespace Sys } // namespace PD diff --git a/include/pd/graphics/lithium.hpp b/include/pd/graphics/lithium.hpp index d3f2a00..8747868 100644 --- a/include/pd/graphics/lithium.hpp +++ b/include/pd/graphics/lithium.hpp @@ -26,6 +26,7 @@ SOFTWARE. #include #include +#include #include #include #include @@ -46,32 +47,6 @@ enum LITextFlags_ { namespace PD { namespace LI { -/// @brief Container that holds top and bottom corners of a quad -class Rect { - public: - Rect() = default; - Rect(const vec4& t, const vec4& b) { - top = t; - bot = b; - } - Rect(const vec2& tl, const vec2& tr, const vec2& bl, const vec2& br) { - top = vec4(tl, tr); - bot = vec4(bl, br); - } - ~Rect() = default; - - vec4 Top() const { return top; } - vec4 Bot() const { return bot; } - - vec2 TopLeft() const { return vec2(top[0], top[1]); } - vec2 TopRight() const { return vec2(top[2], top[3]); } - vec2 BotLeft() const { return vec2(bot[0], bot[1]); } - vec2 BotRight() const { return vec2(bot[2], bot[3]); } - - private: - vec4 top; - vec4 bot; -}; class Font : public SmartCtor { public: class Codepoint { @@ -172,7 +147,7 @@ class Vertex { /// @brief Required to Set the TexENV enum RenderMode { RenderMode_RGBA, - RenderMode_SysFont, + RenderMode_Font, }; /// @brief Reform the Drawcommand by generating the Vertexbuffer into it class Command : public SmartCtor { @@ -362,6 +337,7 @@ class Renderer : public SmartCtor { void SetColor(u32 col); void SetPos(const vec2& pos); + void SetLayer(int l); void SetUnused() { used = false; } bool Used() const { return used; } @@ -397,6 +373,10 @@ class Renderer : public SmartCtor { area_size = bottom ? bot->GetSize() : top->GetSize(); } + Screen::Screen_ CurrentScreen() const { + return bottom ? Screen::Bottom : Screen::Top; + } + void Rotation(float v) { rot = v; } float Rotation() const { return rot; } void TextScale(float v) { text_size = v; } @@ -404,6 +384,7 @@ class Renderer : public SmartCtor { float TextScale() const { return text_size; } void Layer(int v) { current_layer = v; } int Layer() const { return current_layer; } + RenderFlags& GetFlags() { return flags; } void Font(Font::Ref v) { font = v; font_update = true; @@ -424,7 +405,7 @@ class Renderer : public SmartCtor { /// @param color Color /// @param uv UV Map void DrawRect(const vec2& pos, const vec2& size, u32 color, - const vec4& uv = vec4(0.f, 1.f, 1.f, 0.f)); + const Rect& uv = vec4(0.f, 1.f, 1.f, 0.f)); /// @brief Draw a Solid Rect (uses white tex) /// @note acts as a simplified Draw rect Wrapper /// @param pos Position @@ -488,7 +469,7 @@ class Renderer : public SmartCtor { /// @brief Automatically sets up a command void SetupCommand(Command::Ref cmd); /// @brief Creates a default Quad Render Command - void QuadCommand(Command::Ref cmd, const Rect& quad, const vec4& uv, u32 col); + void QuadCommand(Command::Ref cmd, const Rect& quad, const Rect& uv, u32 col); /// @brief Create a Default Triangle void TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b, const vec2& c, u32 col); diff --git a/include/pd/graphics/rect.hpp b/include/pd/graphics/rect.hpp new file mode 100644 index 0000000..0467e2f --- /dev/null +++ b/include/pd/graphics/rect.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +namespace PD { +namespace LI { +/// @brief Container that holds top and bottom corners of a quad +class Rect { + public: + Rect() = default; + Rect(const vec4& t, const vec4& b) { + top = t; + bot = b; + } + Rect(const vec2& tl, const vec2& tr, const vec2& bl, const vec2& br) { + top = vec4(tl, tr); + bot = vec4(bl, br); + } + /// This Constructor Fixes the issue of rewriting some Stuff in the Text + /// Renderer + Rect(const vec4& uv) { + top = vec4(uv.x(), uv.y(), uv.z(), uv.y()); + bot = vec4(uv.x(), uv.w(), uv.z(), uv.w()); + } + ~Rect() = default; + + vec4 Top() const { return top; } + vec4 Bot() const { return bot; } + + Rect& Top(const vec4& v) { + top = v; + return *this; + } + Rect& Bot(const vec4& v) { + bot = v; + return *this; + } + + vec2 TopLeft() const { return vec2(top[0], top[1]); } + vec2 TopRight() const { return vec2(top[2], top[3]); } + vec2 BotLeft() const { return vec2(bot[0], bot[1]); } + vec2 BotRight() const { return vec2(bot[2], bot[3]); } + + Rect& TopLeft(const vec2& v) { + top[0] = v[0]; + top[1] = v[1]; + return *this; + } + Rect& TopRight(const vec2& v) { + top[2] = v[0]; + top[3] = v[1]; + return *this; + } + Rect& BotLeft(const vec2& v) { + bot[0] = v[0]; + bot[1] = v[1]; + return *this; + } + Rect& BotRight(const vec2& v) { + bot[2] = v[0]; + bot[3] = v[1]; + return *this; + } + + void SwapVec2XY() { + for (int i = 0; i < 4; i += 2) { + float t = top[i]; + top[i] = top[i + 1]; + top[i + 1] = t; + t = bot[i]; + bot[i] = bot[i + 1]; + bot[i + 1] = t; + } + } + + private: + vec4 top; + vec4 bot; +}; +} // namespace LI +} // namespace PD \ No newline at end of file diff --git a/include/pd/graphics/spritesheet.hpp b/include/pd/graphics/spritesheet.hpp new file mode 100644 index 0000000..964ddac --- /dev/null +++ b/include/pd/graphics/spritesheet.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace PD { +class SpriteSheet { + public: + SpriteSheet() {} + SpriteSheet(const std::string& path) { this->LoadFile(path); } + ~SpriteSheet(); + + void LoadFile(const std::string& path); + Texture::Ref Get(int idx); + int NumTextures() const; + + Texture::Ref operator[](int idx) { return Get(idx); } + + private: + std::vector textures; +}; +} // namespace PD \ No newline at end of file diff --git a/include/pd/graphics/texture.hpp b/include/pd/graphics/texture.hpp index 85ab153..f6bd1a6 100644 --- a/include/pd/graphics/texture.hpp +++ b/include/pd/graphics/texture.hpp @@ -27,6 +27,7 @@ SOFTWARE. #include #include +#include #include namespace PD { @@ -46,8 +47,12 @@ class Texture : public SmartCtor { Texture() : uv(0.f, 1.f, 1.f, 0.f) {} /// @brief Load file Constructor /// @param path path to file - Texture(const std::string& path) : uv(0.f, 1.f, 1.f, 0.f) { - this->LoadFile(path); + Texture(const std::string& path, bool t3x = false) : uv(0.f, 1.f, 1.f, 0.f) { + if (t3x) { + this->LoadT3X(path); + } else { + this->LoadFile(path); + } } /// @brief Load Memory constructor /// @param data File Data reference @@ -66,7 +71,11 @@ class Texture : public SmartCtor { this->LoadPixels(data, w, h, type, filter); } /// @brief Deconstructor (aka auto delete) - ~Texture() { Delete(); } + ~Texture() { + if (autounload) { + Delete(); + } + } /// @brief Deletes image (if not already unloaded) void Delete(); @@ -86,11 +95,17 @@ class Texture : public SmartCtor { void LoadPixels(const std::vector& data, int w, int h, Type type = RGBA32, Filter filter = NEAREST); + /// @brief Load a texture of a T3X File + /// @note This is used for single texture T3X + /// Not for SpriteSheets + /// @param path path to .t3x file + void LoadT3X(const std::string& path); + /// @brief Input a Texture that you had set up on your own /// @param tex Texture reference (deletes itself) /// @param rszs The size of the source image /// @param uvs Your uv Setup - void LoadExternal(C3D_Tex* tex, vec2 rszs, vec4 uvs) { + void LoadExternal(C3D_Tex* tex, vec2 rszs, LI::Rect uvs) { this->Delete(); this->tex = tex; this->size = rszs; @@ -105,19 +120,23 @@ class Texture : public SmartCtor { } vec2 GetSize() const { return size; } C3D_Tex* GetTex() const { return tex; }; - vec4 GetUV() const { return uv; } + LI::Rect GetUV() const { return uv; } bool IsValid() const { return tex != 0; } + bool AutoUnLoad() const { return autounload; } + void AutoUnLoad(bool v) { autounload = v; } + operator C3D_Tex*() const { return tex; } operator vec2() const { return size; } - operator vec4() const { return uv; } + operator LI::Rect() const { return uv; } operator bool() const { return tex != 0; } private: void MakeTex(std::vector& buf, int w, int h, Type type = RGBA32, Filter filter = NEAREST); vec2 size; - vec4 uv; + LI::Rect uv; C3D_Tex* tex = nullptr; + bool autounload = true; }; } // namespace PD \ No newline at end of file diff --git a/include/pd/overlays/message_mgr.hpp b/include/pd/overlays/message_mgr.hpp index de7885e..ec4e34a 100644 --- a/include/pd/overlays/message_mgr.hpp +++ b/include/pd/overlays/message_mgr.hpp @@ -21,16 +21,16 @@ class MessageMgr : public PD::SmartCtor { bool ShouldBeRemoved() const { return (tbr && pos.IsFinished()) || kill; } private: - PD::Color col_bg; // Background Color - PD::Color col_text; // Text Color - float lifetime = 0.f; // LifeTime - PD::Tween pos; // Position effect - std::string title; // Title - std::string msg; // Message - vec2 size; // Size of the Background - bool tbr = false; // To be Removed ? - bool kill = false; // Instant Kill - int s = 0; // Slot + PD::Color col_bg; // Background Color + PD::Color col_text; // Text Color + float lifetime = 0.f; // LifeTime + PD::Tween pos; // Position effect + std::string title; // Title + std::string msg; // Message + vec2 size; // Size of the Background + bool tbr = false; // To be Removed ? + bool kill = false; // Instant Kill + int s = 0; // Slot }; MessageMgr(PD::LI::Renderer::Ref r) { ren = r; } ~MessageMgr() {} diff --git a/include/pd/ui7/drawlist.hpp b/include/pd/ui7/drawlist.hpp index 3ad4ec7..ca01312 100644 --- a/include/pd/ui7/drawlist.hpp +++ b/include/pd/ui7/drawlist.hpp @@ -38,7 +38,7 @@ class DrawList : public SmartCtor { void AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2, const UI7Color& clr); void AddText(vec2 pos, const std::string& text, const UI7Color& clr, LITextFlags flags = 0, vec2 box = vec2()); - void AddImage(vec2 pos, Texture::Ref img); + void AddImage(vec2 pos, Texture::Ref img, vec2 size = 0.f); void Clear(); void Process(); @@ -47,9 +47,22 @@ class DrawList : public SmartCtor { void Layer(int v) { layer = v; } private: + /// @brief Base Layer offset (Internal Used) + int BaseLayer() const { return base; } + /// @brief Base Layer offset (Internal Used) + void BaseLayer(int v) { base = v; } + + /// @brief Exopose Renderer here for Menus [DONT KNOW IF THIUS GETS REMOVED] + LI::Renderer::Ref GetRenderer() { return ren; } + + friend class Menu; + friend class Context; + int layer; + int base; LI::Renderer::Ref ren; - std::vector commands; + std::unordered_map static_text; + std::vector> commands; }; } // namespace UI7 } // namespace PD \ No newline at end of file diff --git a/include/pd/ui7/id.hpp b/include/pd/ui7/id.hpp index cbc9559..21de6f1 100644 --- a/include/pd/ui7/id.hpp +++ b/include/pd/ui7/id.hpp @@ -7,13 +7,23 @@ namespace PD { namespace UI7 { class ID { public: - ID(const std::string& text) { id = PD::Strings::FastHash(text); } + ID(const std::string& text) { + id = PD::Strings::FastHash(text); + name = text; + } + ID(const char* text) { + id = PD::Strings::FastHash(text); + name = text; + } ~ID() {} + std::string GetName() const { return name; } + operator u32() const { return id; } private: u32 id; + std::string name; }; } // namespace UI7 } // namespace PD \ No newline at end of file diff --git a/include/pd/ui7/menu.hpp b/include/pd/ui7/menu.hpp index 1d86fd7..203d550 100644 --- a/include/pd/ui7/menu.hpp +++ b/include/pd/ui7/menu.hpp @@ -23,14 +23,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include +#include +#include namespace PD { namespace UI7 { class Menu : public SmartCtor { public: - Menu(u32 id) { + Menu(ID id, Theme* tl, Hid::Ref h) { + linked_theme = tl; + this->inp = h; this->id = id; + this->name = id.GetName(); scrolling[0] = false; scrolling[1] = false; scrollbar[0] = false; @@ -38,10 +44,75 @@ class Menu : public SmartCtor { scroll_allowed[0] = false; scroll_allowed[1] = false; }; - ~Menu() {}; + ~Menu() {} + + /// Objects + void Label(const std::string& label); + bool Button(const std::string& label); + void Checkbox(const std::string& label, bool& v); + void Image(Texture::Ref img, vec2 size = 0.f); + + /// Basic API + void SameLine(); + void Separator(); + void SeparatorText(const std::string& label); + + /// API for Custom Objects + bool HandleScrolling(vec2& pos, const vec2& size); + vec2 Cursor() const { return cursor; } + void Cursor(const vec2& v) { + bcursor = cursor; + cursor = v; + } + void RestoreCursor() { + cursor = bcursor; + bcursor = vec2(); + } + + /// Draw Lists + DrawList::Ref BackList() { return back; } + void BackList(DrawList::Ref v) { back = v; } + DrawList::Ref MainList() { return main; } + void MainList(DrawList::Ref v) { main = v; } + DrawList::Ref FrontList() { return front; } + void FrontList(DrawList::Ref v) { front = v; } + + /// Advanced + void DebugLabels(); + + /// Uneditable Stuff + std::string GetName() const { return name; } + u32 GetID() const { return id; } private: + /// Advanced Handlers + void PreHandler(UI7MenuFlags flags); + void PostHandler(); + /// Basic Settings + vec2 BackupCursor() const { return bcursor; } + void BackupCursor(const vec2& v) { bcursor = v; } + vec2 SameLineCursor() const { return slcursor; } + void SameLineCursor(const vec2& v) { slcursor = v; } + vec4 ViewArea() const { return view_area; } + void ViewArea(const vec4& v) { view_area = v; } + vec2 ScrollOffset() const { return scrolling_off; } + void ScrollOffset(const vec2& v) { scrolling_off = v; } + vec2 ScrollMod() const { return scroll_mod; } + void ScrollMod(const vec2& v) { scroll_mod = v; } + + /// Advanced + void CursorMove(const vec2& szs); + + /// Internal Processing + void Update(float delta); + + /// This ability is crazy useful + friend class Context; + + /// Data + UI7MenuFlags flags = 0; u32 id; + std::string name; vec2 cursor; vec2 bcursor; vec2 slcursor; @@ -65,6 +136,12 @@ class Menu : public SmartCtor { vec2 mouse; vec2 bslpos; vec2 last_size; + + // Theme + Theme* linked_theme; + + // Input Reference + Hid::Ref inp; }; } // namespace UI7 } // namespace PD \ No newline at end of file diff --git a/include/pd/ui7/theme.hpp b/include/pd/ui7/theme.hpp index e1a0d67..ab91a8d 100644 --- a/include/pd/ui7/theme.hpp +++ b/include/pd/ui7/theme.hpp @@ -32,14 +32,14 @@ enum UI7Color_ { UI7Color_Button, UI7Color_ButtonDead, UI7Color_ButtonActive, - UI7Color_ButtonDisabled, + UI7Color_ButtonHovered, UI7Color_Text, UI7Color_TextDead, UI7Color_Header, UI7Color_Selector, UI7Color_Checkmark, UI7Color_FrameBackground, - UI7Color_FragmeBackgroundHovered, + UI7Color_FrameBackgroundHovered, UI7Color_Progressbar, UI7Color_ListEven, UI7Color_ListOdd, @@ -115,20 +115,4 @@ class Theme { std::vector> changes; }; } // namespace UI7 -/// Using UI7Color as a Class to be able to -/// define it as struct as well as using it as enum -class UI7Color { - public: - UI7Color() { - /// No Color - } - UI7Color(unsigned int c) { color = c; } - UI7Color(UI7Color_ c) {} - ~UI7Color() {} - - operator u32() const { return color; } - - private: - u32 color; -}; } // namespace PD \ No newline at end of file diff --git a/include/pd/ui7/ui7.hpp b/include/pd/ui7/ui7.hpp index b2d7338..c8ebcd0 100644 --- a/include/pd/ui7/ui7.hpp +++ b/include/pd/ui7/ui7.hpp @@ -23,23 +23,45 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include //// WOW A NON UI/ Header #include #include #include #include #include -#include namespace PD { namespace UI7 { class Context : public SmartCtor { public: - Context() {} + Context(LI::Renderer::Ref ren, Hid::Ref hid) { + this->ren = ren; + this->inp = hid; + Theme::Default(theme); + back = DrawList::New(ren); + front = DrawList::New(ren); + } ~Context() {} + bool BeginMenu(const ID& id, UI7MenuFlags flags = 0); + Menu::Ref GetCurrentMenu(); + void EndMenu(); + + /// Theme Management + Theme& GetTheme() { return theme; } + + /// @brief Update Context (Render menus) + /// @param delta deltatime void Update(float delta); + /// Expose DrawLists + DrawList::Ref BackList() { return back; } + DrawList::Ref FrontList() { return front; } + private: + // Linked Renderer / Hid + LI::Renderer::Ref ren; + Hid::Ref inp; // Timing float delta; float time; @@ -50,11 +72,14 @@ class Context : public SmartCtor { bool debugging; // Menu Handlers std::unordered_map menus; + std::vector amenus; // Active ones Menu::Ref current; // Context DrawList DrawList::Ref debug; DrawList::Ref front; DrawList::Ref back; + // Theme + Theme theme; // Promt Handler }; } // namespace UI7 diff --git a/source/common/app.cpp b/source/common/app.cpp index 3f1024c..0f7cfdd 100644 --- a/source/common/app.cpp +++ b/source/common/app.cpp @@ -28,6 +28,8 @@ SOFTWARE. #include namespace PD { +int App::too; + void App::Run() { this->PreInit(); this->Init(); @@ -45,7 +47,10 @@ void App::Run() { } PD::TT::End("App_MainLoop"); PD::TT::Beg("Ovl_Update"); + renderer->Layer(90); overlay_mgr->Update(dt); + /// Messages have their own special Layer + renderer->Layer(93); msg_mgr->Update(dt); PD::TT::End("Ovl_Update"); renderer->Render(); diff --git a/source/common/io.cpp b/source/common/io.cpp new file mode 100644 index 0000000..e9c20b1 --- /dev/null +++ b/source/common/io.cpp @@ -0,0 +1,26 @@ +#include + +namespace PD { +namespace IO { +std::vector LoadFile2Mem(const std::string& path) { + std::ifstream iff(path, std::ios::binary); + if (!iff) { + return std::vector(); + } + iff.seekg(0, std::ios::end); + size_t szs = iff.tellg(); + iff.seekg(0, std::ios::beg); + std::vector res(szs, 0); + iff.read(reinterpret_cast(res.data()), res.size()); + iff.close(); + return res; +} +u32 HashMemory(const std::vector& data) { + u32 hash = 4477; + for (auto& it : data) { + hash = (hash * 33) + it; + } + return hash; +} +} // namespace IO +} // namespace PD \ No newline at end of file diff --git a/source/common/sys.cpp b/source/common/sys.cpp index d7d35ac..c726684 100644 --- a/source/common/sys.cpp +++ b/source/common/sys.cpp @@ -44,5 +44,8 @@ TT::Res::Ref& GetTraceRef(const std::string& id) { } return pd_sys_tm[id]; } +bool TraceExist(const std::string& id) { + return pd_sys_tm.find(id) != pd_sys_tm.end(); +} TraceMap& GetTraceMap() { return pd_sys_tm; } } // namespace PD::Sys \ No newline at end of file diff --git a/source/graphics/lithium.cpp b/source/graphics/lithium.cpp index 3469d4c..90be6d4 100644 --- a/source/graphics/lithium.cpp +++ b/source/graphics/lithium.cpp @@ -23,6 +23,7 @@ SOFTWARE. #include +#include #include #include #include @@ -45,31 +46,44 @@ void Font::LoadTTF(const std::string& path, int height) { loader.read(reinterpret_cast(buffer), len); loader.close(); stbtt_InitFont(&inf, buffer, 0); - std::vector font_tex(quad * quad * 4); + std::vector font_tex(quad * quad); float scale = stbtt_ScaleForPixelHeight(&inf, pixel_height); int ascent, descent, lineGap; stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap); int baseline = static_cast(ascent * scale); + std::map buf_cache; + auto tex = Texture::New(); vec2 off; - for (int i = 0; i < 255; i++) { - Codepoint c; + for (u32 ii = 0x0000; ii < 0xFFFF; ii++) { + int i = stbtt_FindGlyphIndex(&inf, ii); + if (i == 0) { + continue; + } if (stbtt_IsGlyphEmpty(&inf, i)) { - c.cp(i); - c.tex(tex); - c.invalid(true); - cpmap[i] = c; continue; } + Codepoint c; int w = 0, h = 0, xo = 0, yo = 0; unsigned char* bitmap = stbtt_GetCodepointBitmap(&inf, scale, scale, i, &w, &h, &xo, &yo); int x0, y0, x1, y1; stbtt_GetCodepointBitmapBox(&inf, i, scale, scale, &x0, &y0, &x1, &y1); + u32 hashed_map = IO::HashMemory(std::vector(bitmap, bitmap + (w * h))); + if (buf_cache.find(hashed_map) != buf_cache.end()) { + c = GetCodepoint(buf_cache[hashed_map]); + c.cp(i); + cpmap[i] = c; + free(bitmap); + continue; + } else { + buf_cache[hashed_map] = i; + } + if (off[0] + w > quad) { off[1] += pixel_height; off[0] = 0; @@ -88,24 +102,25 @@ void Font::LoadTTF(const std::string& path, int height) { for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { - int map_pos = ((off[1] + y) * quad + (off[0] + x)) * 4; - - font_tex[map_pos + 0] = 255; - font_tex[map_pos + 1] = 255; - font_tex[map_pos + 2] = 255; - font_tex[map_pos + 3] = bitmap[x + y * w]; + int map_pos = ((off[1] + y) * quad + (off[0] + x)); + font_tex[map_pos] = bitmap[x + y * w]; } } + + free(bitmap); + cpmap[i] = c; + // Small Patch to avoid some possible artifacts off[0] += w + 1; if (off[0] + w > quad) { off[1] += pixel_height; + if (off[1] + pixel_height > quad) { + break; + } off[0] = 0; } - free(bitmap); - cpmap[i] = c; } - tex->LoadPixels(font_tex, quad, quad, Texture::RGBA32, Texture::LINEAR); + tex->LoadPixels(font_tex, quad, quad, Texture::A8, Texture::LINEAR); textures.push_back(tex); } @@ -143,6 +158,7 @@ void Font::LoadSystemFont() { tx->border = 0xffffffff; tx->lodParam = 0; stex->LoadExternal(tx, vec2(tx->width, tx->height), vec4(0, 1, 1, 0)); + stex->AutoUnLoad(false); textures[i] = stex; } std::vector charSet; @@ -192,8 +208,10 @@ void Font::LoadSystemFont() { codepoint.uv(vec4(dat.texcoord.left, dat.texcoord.top, dat.texcoord.right, dat.texcoord.bottom)); - if (textures.at(dat.sheetIndex) != nullptr) { + if (dat.sheetIndex < (int)textures.size()) { codepoint.tex(textures[dat.sheetIndex]); + } else { + codepoint.invalid(true); } codepoint.size(vec2(dat.vtxcoord.right, dat.vtxcoord.bottom)); codepoint.off(0); @@ -250,7 +268,9 @@ void Renderer::StaticText::Setup(Renderer* ren, const vec2& pos, u32 clr, this->pos = pos; this->ren = ren; this->text = StaticObject::New(); - ren->TextCommand(this->text->List(), pos, clr, text, flags, box); + /// Ensure that it also renders Out of Screen i guess + ren->TextCommand(this->text->List(), pos, clr, text, + flags | LITextFlags_RenderOOS, box); OptiCommandList(this->text->List()); } @@ -267,6 +287,8 @@ void Renderer::StaticText::SetPos(const vec2& pos) { text->MoveIt(pos - this->pos); } +void Renderer::StaticText::SetLayer(int layer) { text->ReLayer(layer); } + bool Renderer::InBox(const vec2& pos, const vec2& szs, const vec4& rect) { return (pos[0] < rect[2] || pos[1] < rect[3] || pos[0] + szs[0] > rect[0] || pos[1] + szs[1] > rect[1]); @@ -341,18 +363,14 @@ void Renderer::SetupCommand(Command::Ref cmd) { cmd->Index(cmd_idx++).Layer(current_layer).Tex(current_tex); } -void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, const vec4& uv, +void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, const Rect& uv, u32 col) { cmd->PushIndex(0).PushIndex(1).PushIndex(2); cmd->PushIndex(0).PushIndex(2).PushIndex(3); - cmd->PushVertex( - Vertex(vec2(quad.Bot().z(), quad.Bot().w()), vec2(uv.z(), uv.w()), col)); - cmd->PushVertex( - Vertex(vec2(quad.Top().z(), quad.Top().w()), vec2(uv.z(), uv.y()), col)); - cmd->PushVertex( - Vertex(vec2(quad.Top().x(), quad.Top().y()), vec2(uv.x(), uv.y()), col)); - cmd->PushVertex( - Vertex(vec2(quad.Bot().x(), quad.Bot().y()), vec2(uv.x(), uv.w()), col)); + cmd->PushVertex(Vertex(quad.BotRight(), uv.BotRight(), col)); + cmd->PushVertex(Vertex(quad.TopRight(), uv.TopRight(), col)); + cmd->PushVertex(Vertex(quad.TopLeft(), uv.TopLeft(), col)); + cmd->PushVertex(Vertex(quad.BotLeft(), uv.BotLeft(), col)); } void Renderer::TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b, @@ -364,7 +382,6 @@ void Renderer::TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b, cmd->PushVertex(Vertex(c, vec2(1.f, 0.f), col)); } -/// TO BE REWRITTEN void Renderer::TextCommand(std::vector& cmds, const vec2& pos, u32 color, const std::string& text, LITextFlags flags, const vec2& box) { @@ -375,6 +392,23 @@ void Renderer::TextCommand(std::vector& cmds, const vec2& pos, float cfs = (default_font_h * text_size) / (float)font->PixelHeight(); float lh = (float)font->PixelHeight() * cfs; vec2 td; + vec2 rpos = pos; + vec2 rbox = box; + if (flags & (LITextFlags_AlignMid | LITextFlags_AlignRight)) { + td = GetTextDimensions(text); + if (rbox[0] == 0.f) { + rbox[0] = area_size.x(); + } + if (rbox[1] == 0.f) { + rbox[1] = area_size.y(); + } + } + if (flags & LITextFlags_AlignMid) { + rpos = rbox * 0.5 - td * 0.5 + pos; + } + if (flags & LITextFlags_AlignRight) { + rpos[0] = rbox[0] - td[0]; + } std::vector lines; std::istringstream iss(text); @@ -384,10 +418,10 @@ void Renderer::TextCommand(std::vector& cmds, const vec2& pos, } for (auto& it : lines) { - if (pos[1] + off[1] + lh < 0) { + if (rpos[1] + off[1] + lh < 0) { off[1] += lh; continue; - } else if (pos[1] + off[1] > GetViewport().w() && + } else if (rpos[1] + off[1] > GetViewport().w() && !(flags & LITextFlags_RenderOOS)) { // Break cause next lines would be out of screen break; @@ -396,9 +430,7 @@ void Renderer::TextCommand(std::vector& cmds, const vec2& pos, auto cmd = Command::New(); current_tex = font->GetCodepoint(wline[0]).tex(); SetupCommand(cmd); - if (font->SystemFont()) { - cmd->Rendermode(RenderMode_SysFont); - } + cmd->Rendermode(RenderMode_Font); for (auto& jt : wline) { auto cp = font->GetCodepoint(jt); if (cp.invalid() && jt != '\n' && jt != '\t') { @@ -409,9 +441,7 @@ void Renderer::TextCommand(std::vector& cmds, const vec2& pos, cmd = Command::New(); current_tex = cp.tex(); SetupCommand(cmd); - if (font->SystemFont()) { - cmd->Rendermode(RenderMode_SysFont); - } + cmd->Rendermode(RenderMode_Font); } if (jt == '\t') { off[0] += 16 * cfs; @@ -421,13 +451,13 @@ void Renderer::TextCommand(std::vector& cmds, const vec2& pos, if (flags & LITextFlags_Shaddow) { // Draw Rect rec = CreateRect( - pos + vec2(off[0] + 1, off[1] + (cp.off() * cfs)) + 1, + rpos + vec2(off[0] + 1, off[1] + (cp.off() * cfs)) + 1, cp.size() * cfs, 0.f); QuadCommand(cmd, rec, cp.uv(), 0xff111111); current_layer++; } // Draw - Rect rec = CreateRect(pos + off + vec2(0, (cp.off() * cfs)), + Rect rec = CreateRect(rpos + off + vec2(0, (cp.off() * cfs)), cp.size() * cfs, 0.f); QuadCommand(cmd, rec, cp.uv(), color); current_layer = lr; @@ -450,7 +480,6 @@ vec4 Renderer::GetViewport() { return vec4(0, 0, screen[0], screen[1]); } -/// TO BE REWRITTEN vec2 Renderer::GetTextDimensions(const std::string& text) { if (!font) { // No font no size (oder so) @@ -516,7 +545,8 @@ vec2 Renderer::GetTextDimensions(const std::string& text) { void Renderer::UpdateRenderMode(const RenderMode& mode) { C3D_TexEnv* env = C3D_GetTexEnv(0); switch (mode) { - case RenderMode_SysFont: + case RenderMode_Font: + /// Sets Only Alpha Using the Color and Replase RGB with vertex color C3D_TexEnvInit(env); C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR); C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE); @@ -526,6 +556,7 @@ void Renderer::UpdateRenderMode(const RenderMode& mode) { // Fall trough instead of defining twice case RenderMode_RGBA: default: + /// Use Texture for RGBA and vertexcolor for visibility C3D_TexEnvInit(env); C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0); C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); @@ -536,23 +567,17 @@ void Renderer::UpdateRenderMode(const RenderMode& mode) { void Renderer::RenderOn(bool bot) { C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, (bot ? &bot_proj : &top_proj)); - C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_ALL); - UpdateRenderMode(RenderMode_RGBA); - int total_vertices = 0; int total_indices = 0; drawcalls = 0; auto& cmds = draw_list[bot]; - commands = cmds.size(); size_t index = 0; - if (flags & RenderFlags_LRS) { OptiCommandList(cmds); } - while (index < cmds.size()) { C3D_Tex* tex = cmds[index]->Tex()->GetTex(); auto mode = cmds[index]->Rendermode(); @@ -572,6 +597,7 @@ void Renderer::RenderOn(bool bot) { } index++; } + C3D_TexBind(0, tex); auto bufInfo = C3D_GetBufInfo(); @@ -580,6 +606,7 @@ void Renderer::RenderOn(bool bot) { C3D_DrawElements(GPU_TRIANGLES, index_idx - start_idx, C3D_UNSIGNED_SHORT, index_buf.data() + start_idx); + drawcalls++; total_vertices += vertex_idx - start_vtx; total_indices += index_idx - start_idx; @@ -629,6 +656,8 @@ void Renderer::Render() { if (Sys::GetTime() - it.second.TimeCreated() > 5) rem.push_back(it.first); } for (auto it : rem) tms.erase(it); + } else { + tms.clear(); } if (flags & RenderFlags_AST) { std::vector rem; @@ -641,11 +670,13 @@ void Renderer::Render() { for (auto& it : rem) { ast.erase(it); } + } else { + ast.clear(); } } void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color, - const vec4& uv) { + const Rect& uv) { if (!InBox(pos, size, GetViewport())) { // Instand abort as it is out of screen return; @@ -659,7 +690,7 @@ void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color, void Renderer::DrawRectSolid(const vec2& pos, const vec2& size, u32 color) { UseTex(); - DrawRect(pos, size, color); + DrawRect(pos, size, color, vec4(0.f, 1.f, 1.f, 0.f)); } void Renderer::DrawTriangle(const vec2& a, const vec2& b, const vec2& c, diff --git a/source/graphics/spritesheet.cpp b/source/graphics/spritesheet.cpp new file mode 100644 index 0000000..5205ac7 --- /dev/null +++ b/source/graphics/spritesheet.cpp @@ -0,0 +1,44 @@ +#include +#include + +namespace PD { +SpriteSheet::~SpriteSheet() { textures.clear(); } + +void SpriteSheet::LoadFile(const std::string& path) { + auto file = IO::LoadFile2Mem(path); + if (file.size() == 0) { + Error("Unable to load file:\n" + path); + } + C3D_Tex* tex = new C3D_Tex; + auto t3x = + Tex3DS_TextureImport(file.data(), file.size(), tex, nullptr, false); + if (!t3x) { + Error("Unable to import:\n" + path); + } + tex->border = 0; + C3D_TexSetWrap(tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); + C3D_TexSetFilter(tex, GPU_LINEAR, GPU_NEAREST); + textures.reserve(Tex3DS_GetNumSubTextures(t3x) + 1); + for (int i = 0; i < (int)Tex3DS_GetNumSubTextures(t3x); i++) { + auto t = Texture::New(); + auto st = Tex3DS_GetSubTexture(t3x, i); + LI::Rect uv(vec2(st->left, st->top), vec2(st->right, st->top), + vec2(st->left, st->bottom), vec2(st->right, st->bottom)); + if (st->top < st->bottom) { + uv.SwapVec2XY(); + } + t->LoadExternal(tex, vec2(st->width, st->height), uv); + textures.push_back(t); + } +} + +Texture::Ref SpriteSheet::Get(int idx) { + if (idx >= (int)textures.size()) { + Error("Trying to Access Texture " + std::to_string(idx + 1) + " of " + + std::to_string(NumTextures())); + } + return textures[idx]; +} + +int SpriteSheet::NumTextures() const { return textures.size(); } +} // namespace PD \ No newline at end of file diff --git a/source/graphics/texture.cpp b/source/graphics/texture.cpp index e0742e4..21dff2b 100644 --- a/source/graphics/texture.cpp +++ b/source/graphics/texture.cpp @@ -24,12 +24,14 @@ SOFTWARE. #include <3ds.h> #include +#include +#include +#include #include #include #include #include -#include namespace PD { GPU_TEXCOLOR GetTexFmt(Texture::Type type) { @@ -54,36 +56,6 @@ void Texture::MakeTex(std::vector& buf, int w, int h, Texture::Type type, Filter filter) { // Don't check here as check done before int bpp = GetBPP(type); - if (bpp == 4) { - // RGBA -> Abgr - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - int pos = (x + y * w) * bpp; - auto r = buf[pos + 0]; - auto g = buf[pos + 1]; - auto b = buf[pos + 2]; - auto a = buf[pos + 3]; - buf[pos + 0] = a; - buf[pos + 1] = b; - buf[pos + 2] = g; - buf[pos + 3] = r; - } - } - } else if (bpp == 3) { - // RGBA -> Abgr - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - int pos = (x + y * w) * bpp; - auto r = buf[pos + 0]; - auto g = buf[pos + 1]; - auto b = buf[pos + 2]; - buf[pos + 0] = b; - buf[pos + 1] = g; - buf[pos + 2] = r; - } - } - } - vec2 tex_size(w, h); // Pow2 if (!PD::BitUtil::IsSingleBit(w)) { @@ -95,10 +67,8 @@ void Texture::MakeTex(std::vector& buf, int w, int h, Texture::Type type, this->size.x() = (u16)w; this->size.y() = (u16)h; - this->uv.x() = 0.0f; - this->uv.y() = 1.0f; - this->uv.z() = ((float)w / (float)tex_size.x()); - this->uv.w() = 1.0 - ((float)h / (float)tex_size.y()); + this->uv = vec4(0.f, 1.f, ((float)w / (float)tex_size.x()), + 1.0 - ((float)h / (float)tex_size.y())); // Texture Setup auto fltr = (filter == NEAREST ? GPU_NEAREST : GPU_LINEAR); @@ -109,7 +79,9 @@ void Texture::MakeTex(std::vector& buf, int w, int h, Texture::Type type, memset(tex->data, 0, tex->size); - if (bpp == 3 || bpp == 4) { + /// Probably Remove this if statement in future + /// This are the things confirmed as working + if (bpp == 3 || bpp == 4 || bpp == 1) { for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int dst_pos = ((((y >> 3) * ((int)tex_size.x() >> 3) + (x >> 3)) << 6) + @@ -117,13 +89,13 @@ void Texture::MakeTex(std::vector& buf, int w, int h, Texture::Type type, ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * bpp; int src_pos = (y * w + x) * bpp; - - memcpy(&((u8*)tex->data)[dst_pos], &buf[src_pos], bpp); + /// Best idea i had + for (int i = 0; i < bpp; i++) { + ((u8*)tex->data)[dst_pos + bpp - 1 - i] = buf[src_pos + i]; + } } } C3D_TexFlush(tex); - } else if (bpp == 1) { - C3D_TexLoadImage(tex, buf.data(), GPU_TEXFACE_2D, 0); } tex->border = 0x00000000; @@ -200,4 +172,23 @@ void Texture::LoadPixels(const std::vector& pixels, int w, int h, Type type, std::vector cpy(pixels); MakeTex(cpy, w, h, type, filter); } + +void Texture::LoadT3X(const std::string& path) { + this->Delete(); + auto file = IO::LoadFile2Mem(path); + if (file.size() == 0) { + Error("Unable to load file:\n" + path); + } + this->tex = new C3D_Tex; + auto t3x = + Tex3DS_TextureImport(file.data(), file.size(), this->tex, nullptr, false); + if (!t3x) { + Error("Unable to import:\n" + path); + } + auto st = Tex3DS_GetSubTexture(t3x, 0); + this->uv = vec4(st->left, st->top, st->right, st->bottom); + this->size[0] = st->width; + this->size[1] = st->height; + Tex3DS_TextureFree(t3x); +} } // namespace PD \ No newline at end of file diff --git a/source/overlays/keyboard.cpp b/source/overlays/keyboard.cpp index 3080bfb..167eca2 100644 --- a/source/overlays/keyboard.cpp +++ b/source/overlays/keyboard.cpp @@ -484,8 +484,6 @@ void Keyboard::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) { /// Process Controller Movement Movement(inp); - /// Declare RenderLayer (10 above the latest) - ren->Layer(ren->Layer() + 10); /// Update animations flymgr.Update(delta); selector.Update(delta); diff --git a/source/overlays/message_mgr.cpp b/source/overlays/message_mgr.cpp index 7e33958..a9b3949 100644 --- a/source/overlays/message_mgr.cpp +++ b/source/overlays/message_mgr.cpp @@ -104,8 +104,6 @@ void MessageMgr::Push(const std::string& title, const std::string& text) { } void MessageMgr::Update(float delta) { - // Go two layers up and Render on Top - ren->Layer(ren->Layer() + 2); ren->OnScreen(Screen::Top); for (size_t i = 0; i < msgs.size(); i++) { // Update the Animation Handlers and Move older diff --git a/source/overlays/performance.cpp b/source/overlays/performance.cpp index 693e217..7a794d2 100644 --- a/source/overlays/performance.cpp +++ b/source/overlays/performance.cpp @@ -12,8 +12,7 @@ void Performance::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) { ren->OnScreen(Screen::Top); ren->TextScale(0.6); vec2 pos; - Line(pos, std::format("FPS {:.1f} FPS / {:.2f}ms", 1000.f / delta, delta), - ren); + Line(pos, std::format("{:.1f} FPS / {:.2f}ms", 1000.f / delta, delta), ren); Line(pos, "Ren [AVG]: " + TSA("LI_RenderAll"), ren); Line(pos, "App [AVG]: " + TSA("App_MainLoop"), ren); Line(pos, "Ovl [AVG]: " + TSA("Ovl_Update"), ren); diff --git a/source/ui7/drawlist.cpp b/source/ui7/drawlist.cpp index bcc6175..87bc9a6 100644 --- a/source/ui7/drawlist.cpp +++ b/source/ui7/drawlist.cpp @@ -21,6 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include namespace PD { @@ -31,11 +32,12 @@ void DrawList::AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr) { } auto rect = ren->CreateRect(pos, szs, 0.f); auto cmd = LI::Command::New(); - ren->SetupCommand(cmd); ren->UseTex(); + ren->SetupCommand(cmd); cmd->Layer(layer); ren->QuadCommand(cmd, rect, vec4(0.f, 1.f, 1.f, 0.f), clr); - commands.push_back(cmd); + commands.push_back( + std::make_pair(ren->CurrentScreen() == Screen::Bottom, cmd)); } void DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2, @@ -44,41 +46,82 @@ void DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2, return; } auto cmd = LI::Command::New(); - ren->SetupCommand(cmd); ren->UseTex(); + ren->SetupCommand(cmd); cmd->Layer(layer); ren->TriangleCommand(cmd, pos0, pos1, pos2, clr); - commands.push_back(cmd); + commands.push_back( + std::make_pair(ren->CurrentScreen() == Screen::Bottom, cmd)); } void DrawList::AddText(vec2 pos, const std::string& text, const UI7Color& clr, LITextFlags flags, vec2 box) { + u32 id = Strings::FastHash(text); + auto e = static_text.find(id); + if (e == static_text.end()) { + static_text[id] = LI::Renderer::StaticText::New(); + e = static_text.find(id); + } + if (!e->second->IsSetup()) { + e->second->Setup(&(*ren), pos, clr, text, flags, box); + } + e->second->SetPos(pos); + e->second->SetColor(clr); + e->second->SetLayer(base + layer); + e->second->Draw(); + + ////// STILL LEAVING THE OLD CODE BELOW AS IT IS MAYBE NEEDED ////// + ////// IF STATIC TEXT SYSTEM SHOULD HAVE AN DISABLE OPTION ////// + // Dont create a Command here as TextCommand has autosetup // cause it needs to generate multiple commands if // Font uses multiple textures - ren->TextCommand(commands, pos, clr, text, flags, box); + // Oh and Handle Layer management here as well + // int l = ren->Layer(); + // ren->Layer(layer); + // std::vector cmds; + // ren->TextCommand(cmds, pos, clr, text, flags, box); + // ren->Layer(l); + // for (auto c : cmds) { + // commands.push_back( + // std::make_pair(ren->CurrentScreen() == Screen::Bottom, c)); + // } } -void DrawList::AddImage(vec2 pos, Texture::Ref img) { - if (!ren->InBox(pos, img->GetSize(), ren->GetViewport())) { +void DrawList::AddImage(vec2 pos, Texture::Ref img, vec2 size) { + size = size == 0.f ? img->GetSize() : size; + if (!ren->InBox(pos, size, ren->GetViewport())) { return; } - auto rect = ren->CreateRect(pos, img->GetSize(), 0.f); + auto rect = ren->CreateRect(pos, size, 0.f); auto cmd = LI::Command::New(); - ren->SetupCommand(cmd); ren->UseTex(img); + ren->SetupCommand(cmd); cmd->Layer(layer); - ren->QuadCommand(cmd, rect, vec4(0.f, 1.f, 1.f, 0.f), 0xffffffff); - commands.push_back(cmd); + ren->QuadCommand(cmd, rect, img->GetUV(), 0xffffffff); + commands.push_back( + std::make_pair(ren->CurrentScreen() == Screen::Bottom, cmd)); } void DrawList::Clear() { commands.clear(); } void DrawList::Process() { - // UI7 Commands Use LI7 as default feature - ren->OptiCommandList(commands); for (auto command : commands) { - ren->PushCommand(command); + command.second->Layer(command.second->Layer() + base); + ren->OnScreen(command.first ? Screen::Bottom : Screen::Top); + ren->PushCommand(command.second); + } + commands.clear(); + layer = 0; + std::vector rem; + for (auto it : static_text) { + if (!it.second->Used()) { + rem.push_back(it.first); + } + it.second->SetUnused(); + } + for (auto& it : rem) { + static_text.erase(it); } } } // namespace UI7 diff --git a/source/ui7/menu.cpp b/source/ui7/menu.cpp new file mode 100644 index 0000000..78e869f --- /dev/null +++ b/source/ui7/menu.cpp @@ -0,0 +1,331 @@ +#include +#include +#include + +////////////////////////////// +//////// OBJECT SETUP //////// +////// Setup Variables /////// +///////// Move Cursor //////// +//// Check Scrolling State /// +/////// Handle Controls ////// +////////// Render //////////// +////////////////////////////// + +namespace PD { +namespace UI7 { +void UI7::Menu::Label(const std::string& label) { + vec2 size = this->back->GetRenderer()->GetTextDimensions(label); + vec2 pos = Cursor(); + CursorMove(size - vec2(0, 4)); // Fix to make gap not to large + + if (HandleScrolling(pos, size)) { + return; + } + + /// To Draw a Label above the head bar you should + /// use the m->GetFrontList() instead + + main->AddText(pos, label, linked_theme->Get(UI7Color_Text), 0, + vec2(view_area.z(), 20)); +} + +bool UI7::Menu::Button(const std::string& label) { + bool ret = false; + auto tszs = this->back->GetRenderer()->GetTextDimensions(label); + vec2 size = tszs + vec2(8, 4); + vec2 pos = Cursor(); + UI7Color clr = UI7Color_Button; + CursorMove(size); + /////// SCROLLING HANDLER HERE //////// + if (HandleScrolling(pos, size)) { + return false; + } + /// CONTROLS /// + if (has_touch) { + if (inp->IsHeld(inp->Touch) && + LI::Renderer::InBox(inp->TouchPos(), vec4(pos, size))) { + clr = UI7Color_ButtonHovered; + } + if (inp->IsUp(inp->Touch) && + LI::Renderer::InBox(inp->TouchPosLast(), vec4(pos, size))) { + clr = UI7Color_ButtonActive; + ret = true; + } + } + /// Rendering /// + main->AddRectangle(pos, size, linked_theme->Get(clr)); + main->AddText(pos + size * 0.5 - tszs * 0.5, label, + linked_theme->Get(UI7Color_Text)); + return ret; +} + +void UI7::Menu::Checkbox(const std::string& label, bool& v) { + vec2 pos = Cursor(); + vec2 tdim = front->ren->GetTextDimensions(label); + vec2 cbs(18); + vec2 size = cbs + vec2(tdim.x() + 5, 0); + CursorMove(size); + + if (HandleScrolling(pos, size)) { + return; + } + + UI7Color cbbg = UI7Color_FrameBackground; + + if (has_touch) { + if (inp->IsHeld(inp->Touch) && + LI::Renderer::InBox(inp->TouchPos(), vec4(pos, size))) { + cbbg = UI7Color_FrameBackgroundHovered; + } + if (inp->IsUp(inp->Touch) && + LI::Renderer::InBox(inp->TouchPosLast(), vec4(pos, size))) { + cbbg = UI7Color_FrameBackgroundHovered; + v = !v; + } + } + + main->AddRectangle(pos, cbs, linked_theme->Get(cbbg)); + if (v) { + main->AddRectangle(pos + 2, cbs - 4, linked_theme->Get(UI7Color_Checkmark)); + } + main->AddText(pos + vec2(cbs.x() + 5, cbs.y() * 0.5 - tdim.y() * 0.5), label, + linked_theme->Get(UI7Color_Text)); +} + +void UI7::Menu::Image(Texture::Ref img, vec2 size) { + /// Variable Setup Stage /// + size = size == 0.f ? img->GetSize() : size; + vec2 pos = Cursor(); + CursorMove(size); + /// Scrolling Handler /// + if (HandleScrolling(pos, size)) { + return; + } + /// Rendering Stage /// + main->AddImage(pos, img, size); +} + +void UI7::Menu::DebugLabels() { + std::stringstream s; + s << "Name: " << name << " ["; + s << std::hex << std::setw(8) << std::setfill('0') << id; + s << std::dec << "]"; + this->Label(s.str()); + this->Label( + "Pre: " + + Strings::FormatNanos( + Sys::GetTraceRef("MPRE_" + name)->GetProtocol()->GetAverage())); + this->Label( + "Post: " + + Strings::FormatNanos( + Sys::GetTraceRef("MPOS_" + name)->GetProtocol()->GetAverage())); + this->Label( + "Update: " + + Strings::FormatNanos( + Sys::GetTraceRef("MUPT_" + name)->GetProtocol()->GetAverage())); + this->Label( + "MUser: " + + Strings::FormatNanos( + Sys::GetTraceRef("MUSR_" + name)->GetProtocol()->GetAverage())); +} + +void UI7::Menu::Update(float delta) { + TT::Scope st("MUPT_" + name); + this->back->BaseLayer(30); + this->back->Process(); + this->main->BaseLayer(40); + this->main->Process(); + this->front->BaseLayer(50); + this->front->Process(); +} + +void UI7::Menu::CursorMove(const vec2& size) { + last_size = size; + slcursor = cursor + vec2(size[0] + 5, 0); + if (bslpos[1]) { + cursor = vec2(5, cursor[1] + bslpos[1] + 5); + bslpos = vec2(); + } else { + cursor = vec2(5, cursor[1] + size[1] + 5); + } + max = vec2(slcursor[0], cursor[1]); +} + +void UI7::Menu::PreHandler(UI7MenuFlags flags) { + TT::Scope st("MPRE_" + name); + TT::Beg("MUSR_" + name); + Cursor(vec2(5, 5)); + this->flags = flags; + this->scrolling[0] = flags & UI7MenuFlags_HzScrolling; + this->scrolling[1] = flags & UI7MenuFlags_VtScrolling; + has_touch = main->GetRenderer()->CurrentScreen() == Screen::Bottom; + if (!(flags & UI7MenuFlags_NoBackground)) { + back->AddRectangle(0, view_area.zw(), UI7Color_Background); + } + if (!(flags & UI7MenuFlags_NoTitlebar)) { + tbh = front->GetRenderer()->TextScale() * 30.f; + front->AddRectangle(0, vec2(view_area.z(), tbh), + linked_theme->Get(UI7Color_Header)); + vec2 tpos(5, tbh * 0.5 - front->ren->GetTextDimensions(name).y() * 0.5); + LITextFlags tflags = LITextFlags_None; + if (flags & UI7MenuFlags_CenterTitle) { + tpos = 0; + tflags = LITextFlags_AlignMid; + } + front->AddText(tpos, this->name, linked_theme->Get(UI7Color_Text), tflags, + vec2(view_area.z(), tbh)); + Cursor(vec2(5, tbh + 5)); + } +} + +void UI7::Menu::PostHandler() { + TT::Scope st("MPOS_" + name); + if (scrolling[1]) { + scroll_allowed[1] = (max[1] > 235); + scrollbar[1] = scroll_allowed[1]; + + if (scrollbar[1]) { + /// Setup Some Variables hare [they are self described] + int screen_w = view_area.z(); + int tsp = 5 + tbh; + int slider_w = 4; + int szs = view_area.w() - tsp - 5; + /// Actually dont have a Horizontal bar yet + if (scrollbar[0]) szs -= slider_w - 2; + int lslider_h = 20; // Dont go less heigt for the drag + float slider_h = (szs - 4) * (float(szs - 4) / max[1]); + /// Visual Slider Height (How it looks in the end) + int vslider_h = std::clamp(slider_h, float(lslider_h), float(szs - 4)); + + /// Check if we overscroll to the bottom and Auto scroll back... + /// Probably schould use Tween ENgine here + if (scrolling_off[1] > max[1] - view_area[3] && max[1] != 0.f && + max[1] >= view_area[3] - 5) { + scrolling_off[1] -= 3.f; + if (scrolling_off[1] < max[1] - view_area[3]) { + scrolling_off[1] = max[1] - view_area[3]; + } + } + + /// Do the Same as above just for Overscroll back to the top + if (scrolling_off[1] < 0) { + scrolling_off[1] += 3.f; + if (scrolling_off[1] > 0) { + scrolling_off[1] = 0; + } + } + + /// Dont overscroll to much + if (scrolling_off[1] < -40 || + scrolling_off[1] > max[1] - view_area[3] + 40) { + scroll_mod[1] = 0.f; + } + + /// The pain :( + if (has_touch) { + vec2 tpos = inp->TouchPos(); + if (inp->IsDown(inp->Touch)) { + mouse = tpos; + } else if (inp->IsUp(inp->Touch)) { + mouse = vec2(); + } + if (inp->IsHeld(inp->Touch)) { + if (!front->ren->InBox(tpos, vec4(view_area[2] - 13, tbh + 5, 8, + view_area[3] - tbh - 10))) { + if (scrolling_off[1] < max[1] - view_area[3] + 40 && + scrolling_off[1] > -40) { + /// Cursor Mod + float cm = mouse[1] - tpos[1]; + if (scroll_mod[1] <= 4.f && scroll_mod[1] >= -4.f && cm != 0) { + scroll_mod[1] = cm; + } + } + mouse = tpos; + } + } + } + + /// Effect + if (scroll_mod[1] != 0) { + scrolling_off[1] += scroll_mod[1]; + } + if (scroll_mod[1] < 0.f) { + scroll_mod[1] += 0.4f; + if (scroll_mod[1] > 0.f) { + scroll_mod[1] = 0; + } + } + if (scroll_mod[1] > 0.f) { + scroll_mod[1] -= 0.4f; + if (scroll_mod[1] < 0.f) { + scroll_mod[1] = 0; + } + } + + int srpos = + tsp + std::clamp(float(szs - vslider_h - 4) * + (scrolling_off[1] / (max[1] - view_area[3])), + 0.f, float(szs - vslider_h - 4)); + + /// Rendering Stage + front->AddRectangle(vec2(screen_w - 12, tsp), vec2(slider_w * 2, szs), + linked_theme->Get(UI7Color_FrameBackground)); + front->AddRectangle(vec2(screen_w - 10, tsp + 2), vec2(slider_w, szs - 4), + linked_theme->Get(UI7Color_FrameBackgroundHovered)); + front->AddRectangle(vec2(screen_w - 10, srpos + 2), + vec2(slider_w, vslider_h), + linked_theme->Get(UI7Color_Button)); + } + } + TT::End("MUSR_" + name); +} + +void UI7::Menu::SameLine() { + bslpos = last_size; + cursor = slcursor; +} + +void UI7::Menu::Separator() { + vec2 pos = Cursor(); + vec2 size = vec2(view_area.z() - (scrollbar[1] ? 24 : 10), 1); + CursorMove(size); + if (HandleScrolling(pos, size)) { + return; + } + main->AddRectangle(pos, size, linked_theme->Get(UI7Color_TextDead)); +} + +void UI7::Menu::SeparatorText(const std::string& label) { + vec2 size = vec2(view_area.z() - (scrollbar[1] ? 24 : 10), 1); + vec2 tdim = this->back->GetRenderer()->GetTextDimensions(label); + vec2 pos = Cursor(); + CursorMove(vec2(size.x(), tdim.y() - 4)); // Fix to make gap not to large + + if (HandleScrolling(pos, size)) { + return; + } + /// Label pos for better overview + vec2 lpos = pos + vec2((view_area.z() - 10) * 0.5 - tdim.x() * 0.5, 0); + main->AddRectangle(pos + vec2(0, tdim.y() * 0.5), + vec2(lpos.x() - pos.x() - 5, size.y()), + linked_theme->Get(UI7Color_TextDead)); + main->AddRectangle(pos + vec2(lpos.x() + tdim.x(), tdim.y() * 0.5), + vec2(size.x() - (lpos.x() + tdim.x()), size.y()), + linked_theme->Get(UI7Color_TextDead)); + main->AddText(lpos, label, linked_theme->Get(UI7Color_Text), 0, + vec2(view_area.z(), 20)); +} + +bool UI7::Menu::HandleScrolling(vec2& pos, const vec2& size) { + if (scrolling[1]) { + vec2 p = pos; + pos -= vec2(0, scrolling_off.y()); + if (pos.y() > view_area.w() || + (pos.y() + size.y() < tbh - 5 && p.y() > tbh)) { + return true; + } + } + return false; +} +} // namespace UI7 +} // namespace PD \ No newline at end of file diff --git a/source/ui7/theme.cpp b/source/ui7/theme.cpp index ac18e6e..965e71f 100644 --- a/source/ui7/theme.cpp +++ b/source/ui7/theme.cpp @@ -10,12 +10,12 @@ void Theme::Default(Theme& theme) { theme.Set(UI7Color_Button, Color("#111111FF")); theme.Set(UI7Color_ButtonDead, Color("#080808FF")); theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF")); - theme.Set(UI7Color_ButtonDisabled, Color("#222222FF")); + theme.Set(UI7Color_ButtonHovered, Color("#222222FF")); theme.Set(UI7Color_Header, Color("#111111FF")); theme.Set(UI7Color_Selector, Color("#222222FF")); theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF")); theme.Set(UI7Color_FrameBackground, Color("#555555FF")); - theme.Set(UI7Color_FragmeBackgroundHovered, Color("#777777FF")); + theme.Set(UI7Color_FrameBackgroundHovered, Color("#777777FF")); theme.Set(UI7Color_Progressbar, Color("#00FF00FF")); theme.Set(UI7Color_ListEven, Color("#CCCCCCFF")); theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF")); diff --git a/source/ui7/ui7.cpp b/source/ui7/ui7.cpp index e69de29..90f7a68 100644 --- a/source/ui7/ui7.cpp +++ b/source/ui7/ui7.cpp @@ -0,0 +1,52 @@ +#include +#include + +namespace PD { +bool UI7::Context::BeginMenu(const ID& id, UI7MenuFlags flags) { + Assert(!this->current, "You are already in another Menu!"); + Assert(std::find(amenus.begin(), amenus.end(), (u32)id) == amenus.end(), + "Menu Name Already used or\nContext::Update not called!"); + auto menu = this->menus.find(id); + if (menu == this->menus.end()) { + this->menus[id] = Menu::New(id, &theme, inp); + menu = this->menus.find(id); + } + this->current = menu->second; + if (!this->current->BackList()) { + this->current->BackList(DrawList::New(ren)); + } + if (!this->current->MainList()) { + this->current->MainList(DrawList::New(ren)); + } + if (!this->current->FrontList()) { + this->current->FrontList(DrawList::New(ren)); + } + this->current->ViewArea(this->ren->GetViewport()); + this->current->PreHandler(flags); + amenus.push_back(this->current->GetID()); + return true; +} + +UI7::Menu::Ref UI7::Context::GetCurrentMenu() { + Assert(current != nullptr, "Not in a Menu!"); + return current; +} + +void UI7::Context::EndMenu() { + this->current->PostHandler(); + this->current = nullptr; +} + +void UI7::Context::Update(float delta) { + TT::Scope st("UI7_Update"); + Assert(current == nullptr, "Still in a Menu!"); + this->back->BaseLayer(10); + this->back->Process(); + for (auto it : amenus) { + menus[it]->Update(delta); + } + this->front->BaseLayer(60); + this->front->Process(); + this->amenus.clear(); +} +} // namespace PD \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index 4305922..09d9d90 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -27,54 +27,82 @@ SOFTWARE. #include #include -using vec2 = PD::vec2; -using vec3 = PD::vec3; -using vec4 = PD::vec4; - class Test : public PD::App { public: Test() = default; ~Test() = default; void Init() override { + ren = Renderer(); + inp = Input(); test = PD::Texture::New("romfs:/icon.png"); - Overlays()->Push(PD::New(dbg, dbg_screen)); + // Performance Overlay freezes N3DS + // Overlays()->Push(PD::New(dbg, dbg_screen)); font = PD::LI::Font::New(); - // font->LoadSystemFont(); - font->LoadTTF("romfs:/ComicNeue.ttf", 32); - Renderer()->Font(font); - ui7 = PD::UI7::Context::New(); + // font->LoadTTF("romfs:/fonts/ComicNeue.ttf", 32); + font->LoadTTF("romfs:/fonts/JetBrainsMono-Medium.ttf", 32); + ren->Font(font); + ui7 = PD::UI7::Context::New(ren, inp); } bool MainLoop(float delta, float time) override { DrawFancyBG(time); - Renderer()->OnScreen(PD::Screen::Bottom); - Renderer()->DrawRectSolid(0, vec2(320, 240), PD::Color("#222222")); - Renderer()->UseTex(test); - Renderer()->Layer(Renderer()->Layer() + 1); - Renderer()->DrawImage( - Renderer()->GetViewport().zw() * 0.5 - test->GetSize() * 0.5, test); - Renderer()->DrawText(5, 0xffffffff, "Hello World!", LITextFlags_None); - if (Input()->IsDown(PD::Hid::Start)) { + ren->OnScreen(PD::Screen::Bottom); + // ren->DrawRectSolid(0, vec2(320, 240), PD::Color("#222222")); + // ren->Layer(ren->Layer() + 1); + // ren->DrawImage(ren->GetViewport().zw() * 0.5 - test->GetSize() * 0.5, + // test); ren->DrawText(5, 0xffffffff, "Hello World!", LITextFlags_None); + if (ui7->BeginMenu("Test", + UI7MenuFlags_Scrolling | UI7MenuFlags_CenterTitle)) { + auto m = ui7->GetCurrentMenu(); + m->SeparatorText("Menu Timings"); + m->DebugLabels(); + m->SeparatorText("Lithium Settings"); + FlagBox(m, "LI AST", PD::LI::RenderFlags_AST); + FlagBox(m, "LI LRS", PD::LI::RenderFlags_LRS); + FlagBox(m, "LI TMS", PD::LI::RenderFlags_TMS); + m->SeparatorText("UI7 Tests"); + m->Label("This seems to be a label"); + m->Separator(); + m->Button("Button?"); + m->SeparatorText("SeparatorText"); + m->Checkbox("Test", cbtest); + for (int i = 0; i < 10; i++) { + m->Label("Label: " + std::to_string(i)); + } + ui7->EndMenu(); + } + ui7->Update(delta); + if (inp->IsDown(PD::Hid::Start)) { return false; } - if (Input()->IsDown(Input()->A)) { + if (inp->IsDown(inp->A)) { Overlays()->Push(PD::New(dbg, dbg_screen)); Messages()->Push("Test", "Oder SO"); - // what.To(vec2(5, 200)).From(vec2(-100, - // 200)).In(0.5).As(what.EaseInQuad); } - if (Input()->IsUp(Input()->B)) { + if (inp->IsUp(inp->B)) { Overlays()->Push(PD::New(text, state)); - // what.To(vec2(5, 180)).From(vec2(5, 200)).In(0.5).As(what.EaseOutQuad); } return true; } void Deinit() override {} + void FlagBox(PD::UI7::Menu::Ref m, const std::string& label, + PD::LI::RenderFlags flag) { + bool has_flag = ren->GetFlags() & flag; + m->Checkbox(label, has_flag); + if (has_flag != (ren->GetFlags() & flag)) { + if (has_flag) { + ren->GetFlags() |= flag; + } else { + ren->GetFlags() &= ~flag; + } + } + } + void DrawFancyBG(float time) { - Renderer()->DrawRect(vec2(0, 0), vec2(400, 240), 0xff64c9fd); + ren->DrawRect(vec2(0, 0), vec2(400, 240), 0xff64c9fd); for (int i = 0; i < 44; i++) Append(i, vec2(0, 0), vec2(400, 240), time); } @@ -90,7 +118,7 @@ class Test : public PD::App { sin(offset + time) * 10 + 30; float color_effect = 1 - exp(-(index / 11) / 3.0f); - Renderer()->DrawTriangle( + ren->DrawTriangle( vec2(x_position, y_position), vec2(x_position + 300, y_position + (90)), vec2(x_position - 300, y_position + (90)), PD::Color(.94f - .17f * color_effect, .61f - .25f * color_effect, @@ -98,8 +126,13 @@ class Test : public PD::App { } private: + /// Shorter Acess to Renderer / Input + PD::LI::Renderer::Ref ren; + PD::Hid::Ref inp; + /// Other Data PD::Texture::Ref test; bool dbg = false, dbg_screen = false; + bool cbtest = true; std::string text; PD::Keyboard::State state; PD::UI7::Context::Ref ui7; @@ -108,7 +141,7 @@ class Test : public PD::App { }; int main() { - auto app = PD::New(); - app->Run(); + Test app; + app.Run(); return 0; } \ No newline at end of file diff --git a/test/romfs/fonts/ComicNeue.md b/test/romfs/fonts/ComicNeue.md new file mode 100644 index 0000000..80062cf --- /dev/null +++ b/test/romfs/fonts/ComicNeue.md @@ -0,0 +1,93 @@ +Copyright 2014 The Comic Neue Project Authors (https://github.com/crozynski/comicneue) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/test/romfs/ComicNeue.ttf b/test/romfs/fonts/ComicNeue.ttf similarity index 100% rename from test/romfs/ComicNeue.ttf rename to test/romfs/fonts/ComicNeue.ttf diff --git a/test/romfs/fonts/JetBrainsMono-Medium.ttf b/test/romfs/fonts/JetBrainsMono-Medium.ttf new file mode 100644 index 0000000..ad71d92 Binary files /dev/null and b/test/romfs/fonts/JetBrainsMono-Medium.ttf differ diff --git a/test/romfs/fonts/JetBrainsMono.txt b/test/romfs/fonts/JetBrainsMono.txt new file mode 100644 index 0000000..5ceee00 --- /dev/null +++ b/test/romfs/fonts/JetBrainsMono.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE.