Implement Text Rendering
Added STB truetype Switched AssetMgr to use Shared Ptr
This commit is contained in:
@@ -7,7 +7,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED true)
|
||||
|
||||
option(AMY_GOD_DEV "Turn this on if you think you are god" OFF)
|
||||
# THis option should be disabled if you use STB_IMAGE in you main project
|
||||
set(AMY_BUILD_STB_IMAGE "Include STB Image code" CACHE BOOL 1)
|
||||
set(AMY_BUILD_STB_IMAGE 1)
|
||||
set(AMY_BUILD_STB_TRUETYPE 1)
|
||||
set(AMY_WITH_MPG123 "Include MP3 Support" CACHE BOOL 1)
|
||||
|
||||
#add_subdirectory(vendor/libpicasso)
|
||||
@@ -24,6 +25,7 @@ add_library(${PROJECT_NAME} STATIC
|
||||
source/c3d.cpp
|
||||
source/iron/iron.cpp
|
||||
source/iron/drawlist.cpp
|
||||
source/iron/font.cpp
|
||||
source/maths/mat.cpp
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
@@ -38,6 +40,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC m z ctru citro3d mpg123)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC
|
||||
AMY_3DS
|
||||
AMY_STB_IMAGE=${AMY_BUILD_STB_IMAGE}
|
||||
AMY_STB_TT=${AMY_BUILD_STB_TRUETYPE}
|
||||
AMY_WITH_MPG123=${AMY_WITH_MPG123}
|
||||
)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC -Wno-psabi)
|
||||
@@ -45,4 +48,4 @@ target_compile_options(${PROJECT_NAME} PUBLIC -Wno-psabi)
|
||||
add_subdirectory(example)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME})
|
||||
install(DIRECTORY include DESTINATION .)
|
||||
install(DIRECTORY include DESTINATION .)
|
||||
|
||||
@@ -26,4 +26,4 @@ Coming Soon...
|
||||
## Credits
|
||||
|
||||
- [tobid7](https://github.com/tobid7) Lead Developer, Stealing a lot of code of [Palladium](https://github.com/tobid7/palladium)
|
||||
- [nothings](https://github.com/nothings) [stb_image](https://github.com/nothings/stb)
|
||||
- [nothings](https://github.com/nothings) stb_image and stb_truetype from [here](https://github.com/nothings/stb)
|
||||
BIN
example/romfs/ComicNeue.ttf
Normal file
BIN
example/romfs/ComicNeue.ttf
Normal file
Binary file not shown.
93
example/romfs/ComicNeue.txt
Normal file
93
example/romfs/ComicNeue.txt
Normal file
@@ -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.
|
||||
@@ -13,7 +13,11 @@ class Example : public Amy::App {
|
||||
Mgr->AutoLoad("icon", "romfs:/icon.png");
|
||||
Iron::Init();
|
||||
dl = new Iron::Drawlist();
|
||||
Fnt = Iron::Font::New();
|
||||
Fnt->LoadTTF("romfs:/ComicNeue.ttf");
|
||||
dl->SetFont(Fnt);
|
||||
}
|
||||
|
||||
~Example() {
|
||||
delete Top;
|
||||
delete dl;
|
||||
@@ -46,6 +50,7 @@ class Example : public Amy::App {
|
||||
dl->DrawCircleFilled(Amy::fvec2(200, 120), 50, Amy::Color("#ffffff"), 40);
|
||||
dl->DrawSolid();
|
||||
dl->DrawRectFilled(0, 50, Amy::Color(0.f, 1.f, 0.f, 1.f));
|
||||
dl->DrawText(Amy::fvec2(5, 50), "Hello World!", Amy::Color(255, 0, 255));
|
||||
|
||||
Iron::NewFrame();
|
||||
Iron::DrawOn(Top);
|
||||
@@ -57,6 +62,7 @@ class Example : public Amy::App {
|
||||
C3D::Screen* Top;
|
||||
Amy::AssetMgr* Mgr;
|
||||
Iron::Drawlist* dl;
|
||||
Iron::Font::Ref Fnt;
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <amethyst/types.hpp>
|
||||
|
||||
namespace Amy {
|
||||
class Asset {
|
||||
public:
|
||||
Asset() = default;
|
||||
virtual ~Asset() = default;
|
||||
AMY_SHARED(Asset);
|
||||
};
|
||||
} // namespace Amy
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <amethyst/id.hpp>
|
||||
#include <amethyst/texture.hpp>
|
||||
#include <amethyst/types.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Amy {
|
||||
class AssetMgr {
|
||||
@@ -12,7 +13,7 @@ class AssetMgr {
|
||||
|
||||
void AutoLoad(const ID& name, ksr path);
|
||||
|
||||
void Add(const ID& id, Asset* v) {
|
||||
void Add(const ID& id, Asset::Ref v) {
|
||||
if (pAssets.count(id)) {
|
||||
throw std::runtime_error("[amy]: assets: " + id.GetName() +
|
||||
" already exists!");
|
||||
@@ -27,12 +28,12 @@ class AssetMgr {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* Get(const ID& id) {
|
||||
std::shared_ptr<T> Get(const ID& id) {
|
||||
auto r = pAssets.find(id);
|
||||
if (r == pAssets.end()) {
|
||||
throw std::runtime_error("[amy] assets: unable to find " + id.GetName());
|
||||
}
|
||||
if (auto v = dynamic_cast<T*>(r->second)) {
|
||||
if (auto v = std::dynamic_pointer_cast<T>(r->second)) {
|
||||
return v;
|
||||
} else {
|
||||
throw std::runtime_error(id.GetName() + " is not of type " +
|
||||
@@ -46,14 +47,14 @@ class AssetMgr {
|
||||
if (r == pAssets.end()) {
|
||||
throw std::runtime_error("[amy] assets: unable to find " + id.GetName());
|
||||
}
|
||||
return dynamic_cast<T*>(r->second) != nullptr;
|
||||
return std::dynamic_pointer_cast<T>(r->second) != nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t Count() const {
|
||||
size_t ret = 0;
|
||||
for (auto& it : pAssets) {
|
||||
if (dynamic_cast<T*>(it.second)) {
|
||||
if (std::dynamic_pointer_cast<T>(it.second)) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
@@ -61,6 +62,6 @@ class AssetMgr {
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<ID, Asset*> pAssets;
|
||||
std::map<ID, Asset::Ref> pAssets;
|
||||
};
|
||||
} // namespace Amy
|
||||
@@ -42,6 +42,7 @@ class C3D {
|
||||
Shader(ksr path);
|
||||
Shader() {}
|
||||
~Shader();
|
||||
AMY_SHARED(Shader);
|
||||
|
||||
void Load(ksr path);
|
||||
void Load(kvr<uc> data);
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
#include <amethyst/texture.hpp>
|
||||
#include <amethyst/types.hpp>
|
||||
|
||||
using AmyTextFlags = Amy::ui;
|
||||
enum AmyTextFlags_ {
|
||||
AmyTextFlags_None = 0, ///< Do nothing
|
||||
AmyTextFlags_AlignRight = 1 << 0, ///< Align Right of position
|
||||
AmyTextFlags_AlignMid = 1 << 1, ///< Align in the middle of pos and box
|
||||
AmyTextFlags_Shaddow = 1 << 2, ///< Draws the text twice to create shaddow
|
||||
AmyTextFlags_Wrap = 1 << 3, ///< Wrap Text: May be runs better with TMS
|
||||
AmyTextFlags_Short = 1 << 4, ///< Short Text: May be runs better with TMS
|
||||
AmyTextFlags_Scroll = 1 << 5, ///< Not implemented [scoll text if to long]
|
||||
};
|
||||
|
||||
namespace Amy {
|
||||
class Iron {
|
||||
public:
|
||||
@@ -51,7 +62,7 @@ class Iron {
|
||||
bool ScissorOn = false;
|
||||
int Layer = 0;
|
||||
int Index = 0;
|
||||
Texture* Tex = nullptr;
|
||||
Texture::Ref Tex = nullptr;
|
||||
};
|
||||
|
||||
class Font {
|
||||
@@ -59,7 +70,7 @@ class Iron {
|
||||
struct Codepoint {
|
||||
ui Cp = 0;
|
||||
fvec4 Uv;
|
||||
Texture* Tex;
|
||||
Texture::Ref Tex;
|
||||
fvec2 Size;
|
||||
float Offset = 0; // Unused??
|
||||
bool Valid = true;
|
||||
@@ -80,12 +91,13 @@ class Iron {
|
||||
|
||||
fvec2 GetTextBounds(ksr text, float scale);
|
||||
void CmdTextEx(vec<Command::Ref>& cmds, const fvec2& pos, ui color,
|
||||
float scale, ksr text, ui flags = 0, const fvec2& box = 0);
|
||||
void pMakeAtlas(bool final, vec<uc>& pixels, int size, Texture* tex);
|
||||
float scale, ksr text, AmyTextFlags flags = 0,
|
||||
const fvec2& box = 0);
|
||||
void pMakeAtlas(bool final, vec<uc>& pixels, int size, Texture::Ref tex);
|
||||
|
||||
int PxHeight;
|
||||
int PxFactor = 24;
|
||||
vec<Texture*> Textures;
|
||||
vec<Texture::Ref> Textures;
|
||||
std::map<u32, Codepoint> pCodeMap;
|
||||
};
|
||||
|
||||
@@ -108,7 +120,8 @@ class Iron {
|
||||
void Clear();
|
||||
|
||||
void DrawSolid();
|
||||
void DrawTex(Texture* tex) { pTex = tex; }
|
||||
void DrawTex(Texture::Ref tex) { pTex = tex; }
|
||||
void SetFont(Font::Ref fnt) { pCurrentFont = fnt; }
|
||||
|
||||
/** Draw Api */
|
||||
void DrawRect(const fvec2& pos, const fvec2& size, ui color,
|
||||
@@ -160,10 +173,11 @@ class Iron {
|
||||
void clipCmd(Command* ptr);
|
||||
std::vector<Command::Ref> pData;
|
||||
std::vector<fvec2> pPath;
|
||||
Texture* pTex = nullptr;
|
||||
Texture::Ref pTex = nullptr;
|
||||
std::stack<fvec4> ClipRects;
|
||||
Font* pCurrentFont;
|
||||
Font::Ref pCurrentFont;
|
||||
int pLayer = 0;
|
||||
float pFontScale = 0.7;
|
||||
};
|
||||
Iron() = default;
|
||||
~Iron() = default;
|
||||
@@ -173,7 +187,7 @@ class Iron {
|
||||
static void NewFrame();
|
||||
static void DrawOn(C3D::Screen* screen);
|
||||
static void Draw(const std::vector<Command::Ref>& data);
|
||||
static Texture* WhiteTex() { return m_solid; }
|
||||
static Texture::Ref WhiteTex() { return m_solid; }
|
||||
|
||||
/** Static renderer utility funcs */
|
||||
|
||||
@@ -185,7 +199,7 @@ class Iron {
|
||||
const fvec2& c, ui clr);
|
||||
static void CmdConvexPolyFilled(Command* cmd,
|
||||
const std::vector<fvec2>& points, ui clr,
|
||||
Texture* tex);
|
||||
Texture::Ref tex);
|
||||
static bool InBox(const fvec2& pos, const fvec2& size, const fvec4& area);
|
||||
static bool InBox(const fvec2& pos, const fvec4& area);
|
||||
static bool InBox(const fvec2& a, const fvec2& b, const fvec2& c,
|
||||
@@ -206,7 +220,7 @@ class Iron {
|
||||
static C3D::Shader* m_shader;
|
||||
static mat4 m_mtx;
|
||||
static int m_idx, m_vtx;
|
||||
static Texture* m_solid;
|
||||
static Texture::Ref m_solid;
|
||||
static ui VertexCount;
|
||||
static ui IndexCount;
|
||||
};
|
||||
|
||||
@@ -12,10 +12,22 @@ class Texture : public Asset {
|
||||
public:
|
||||
Texture() = default;
|
||||
Texture(ksr path);
|
||||
Texture(C3D_Tex* tex, const ivec2& size, const Rect& uv) {
|
||||
Load(tex, size, uv);
|
||||
}
|
||||
~Texture();
|
||||
AMY_SHARED(Texture);
|
||||
|
||||
void Load(ksr path);
|
||||
void Load(kvr<uc> pixels, int w, int h, int bpp = 4,
|
||||
Image::Format fmt = Image::RGBA);
|
||||
void Load(C3D_Tex* tex, const ivec2& size, const Rect& uv) {
|
||||
Unload();
|
||||
pTex = tex;
|
||||
pSize = size;
|
||||
pUv = uv;
|
||||
// Dont set as loaded as only the root tex can be loaded
|
||||
}
|
||||
void Unload();
|
||||
|
||||
int W() const { return pSize.x; }
|
||||
@@ -26,7 +38,7 @@ class Texture : public Asset {
|
||||
ivec2& Size() { return pSize; }
|
||||
Rect& Uv() { return pUv; }
|
||||
|
||||
C3D_Tex* Ptr() { return pLoaded ? pTex : nullptr; }
|
||||
C3D_Tex* Ptr() { return pTex; }
|
||||
|
||||
void Bind(int reg = 0);
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ bool IsSingleBitNum(ui v);
|
||||
ull GetTimeNano();
|
||||
ull GetTimeMicro();
|
||||
ull GetTimeMilli();
|
||||
void String2U16(us* res, ksr src, size_t max);
|
||||
std::string U16toU8(us* in, size_t max);
|
||||
/**
|
||||
* FNV Hash functiom (32 Bit)
|
||||
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||
|
||||
@@ -6,11 +6,11 @@ namespace Amy {
|
||||
void AssetMgr::AutoLoad(const ID& id, ksr path) {
|
||||
if (path.ends_with(".png") || path.ends_with(".jpg") ||
|
||||
path.ends_with(".bmp")) {
|
||||
Texture* tex = new Texture();
|
||||
auto tex = Texture::New();
|
||||
tex->Load(path);
|
||||
Add(id, tex);
|
||||
} else if (path.ends_with(".shbin")) {
|
||||
C3D::Shader* shader = new C3D::Shader();
|
||||
auto shader = C3D::Shader::New();
|
||||
shader->Load(path);
|
||||
Add(id, shader);
|
||||
} else {
|
||||
|
||||
5079
source/internal/stb_truetype.h
Normal file
5079
source/internal/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -243,30 +243,30 @@ void Iron::Drawlist::DrawConvexPolyFilled(const std::vector<fvec2>& points,
|
||||
|
||||
void Iron::Drawlist::DrawText(const fvec2& pos, const std::string& text,
|
||||
ui color) {
|
||||
/*if (!pCurrentFont) {
|
||||
if (!pCurrentFont) {
|
||||
return;
|
||||
}
|
||||
std::vector<Command::Ref> cmds;
|
||||
pCurrentFont->CmdTextEx(cmds, pos, color, pFontScale, text);
|
||||
for (size_t i = 0; i < cmds.size(); i++) {
|
||||
cmds[i]->Index = pDrawlist.size();
|
||||
cmds[i]->Layer = Layer;
|
||||
AddCommand(std::move(cmds[i]));
|
||||
}*/
|
||||
cmds[i]->Index = pData.size();
|
||||
cmds[i]->Layer = pLayer;
|
||||
Push(std::move(cmds[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void Iron::Drawlist::DrawTextEx(const fvec2& p, const std::string& text,
|
||||
ui color, ui flags, const fvec2& box) {
|
||||
/*if (!pCurrentFont) {
|
||||
if (!pCurrentFont) {
|
||||
return;
|
||||
}
|
||||
std::vector<Command::Ref> cmds;
|
||||
pCurrentFont->CmdTextEx(cmds, p, color, pFontScale, text, flags, box);
|
||||
for (size_t i = 0; i < cmds.size(); i++) {
|
||||
cmds[i]->Index = pDrawlist.size();
|
||||
cmds[i]->Layer = Layer;
|
||||
AddCommand(std::move(cmds[i]));
|
||||
}*/
|
||||
cmds[i]->Index = pData.size();
|
||||
cmds[i]->Layer = pLayer;
|
||||
Push(std::move(cmds[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void Iron::Drawlist::DrawLine(const fvec2& a, const fvec2& b, ui color, int t) {
|
||||
|
||||
@@ -1,17 +1,142 @@
|
||||
#include <amethyst/iron.hpp>
|
||||
#include <amethyst/utils.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
#if AMY_STB_TT == 1
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#endif
|
||||
#include <stb_truetype.h>
|
||||
|
||||
namespace Amy {
|
||||
void Iron::Font::LoadBMF(ksr path) {}
|
||||
|
||||
void Iron::Font::LoadTTF(ksr path, int size) {}
|
||||
void Iron::Font::LoadTTF(ksr path, int size) {
|
||||
auto data = Amy::Utils::LoadFile2Mem(path);
|
||||
LoadTTF(data, size);
|
||||
}
|
||||
|
||||
void Iron::Font::pMakeAtlas(bool final, vec<uc>& font_tex, int texszs,
|
||||
Texture* tex) {
|
||||
void Iron::Font::LoadTTF(const vec<uc> &data, int size) {
|
||||
/**
|
||||
* Some additional Info:
|
||||
* Removed the stbtt get bitmapbox as we dont need to place
|
||||
* the glyps nicely in the tex. next step would be using the free
|
||||
* space on the y axis to get mor glyphs inside
|
||||
*/
|
||||
PxHeight = size;
|
||||
int texszs = Amy::Utils::NextPow2(PxHeight * 16);
|
||||
if (texszs > 1024) {
|
||||
texszs = 1024; // Max size
|
||||
}
|
||||
|
||||
stbtt_fontinfo inf;
|
||||
if (!stbtt_InitFont(&inf, data.data(), 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float scale = stbtt_ScaleForPixelHeight(&inf, PxHeight);
|
||||
|
||||
int ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
|
||||
int baseline = static_cast<int>(ascent * scale);
|
||||
|
||||
// Cache to not render same codepoint tex twice
|
||||
std::map<u32, int> buf_cache;
|
||||
|
||||
std::vector<u8> font_tex(texszs * texszs * 4, 0);
|
||||
auto tex = Texture::New();
|
||||
fvec2 off;
|
||||
|
||||
bool empty = true;
|
||||
|
||||
for (u32 ii = 0x0000; ii <= 0xFFFF; ii++) {
|
||||
int gi = stbtt_FindGlyphIndex(&inf, ii);
|
||||
if (gi == 0) continue;
|
||||
if (stbtt_IsGlyphEmpty(&inf, gi)) continue;
|
||||
|
||||
int w = 0, h = 0, xo = 0, yo = 0;
|
||||
unsigned char *bitmap =
|
||||
stbtt_GetCodepointBitmap(&inf, scale, scale, ii, &w, &h, &xo, &yo);
|
||||
if (!bitmap || w <= 0 || h <= 0) {
|
||||
if (bitmap) free(bitmap);
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 hashed_map =
|
||||
Utils::HashMemory(std::vector<u8>(bitmap, bitmap + (w * h)));
|
||||
if (buf_cache.find(hashed_map) != buf_cache.end()) {
|
||||
Codepoint c = GetCodepoint(buf_cache[hashed_map]);
|
||||
c.Cp = ii;
|
||||
pCodeMap[ii] = c;
|
||||
free(bitmap);
|
||||
continue;
|
||||
} else {
|
||||
buf_cache[hashed_map] = ii;
|
||||
}
|
||||
|
||||
// Next row
|
||||
if (off.x + w > texszs) {
|
||||
off.y += PxHeight;
|
||||
off.x = 0.0f;
|
||||
}
|
||||
// Bake cause we go out of the tex
|
||||
if (off.y + PxHeight > texszs) {
|
||||
pMakeAtlas(false, font_tex, texszs, tex);
|
||||
tex = Texture::New();
|
||||
off = 0;
|
||||
std::fill(font_tex.begin(), font_tex.end(), 0);
|
||||
empty = true;
|
||||
}
|
||||
|
||||
// UVs & Codepoint
|
||||
Codepoint c;
|
||||
fvec4 uvs;
|
||||
// cast the ints to floats and not the floats...
|
||||
// dont know where my mind was when creating the code
|
||||
uvs.x = off.x / static_cast<float>(texszs);
|
||||
uvs.y = off.y / static_cast<float>(texszs);
|
||||
uvs.z = (off.x + w) / static_cast<float>(texszs);
|
||||
uvs.w = (off.y + h) / static_cast<float>(texszs);
|
||||
// Flip (too lazy to place it into the seq at top)
|
||||
uvs.y = 1.f - uvs.y;
|
||||
uvs.w = 1.f - uvs.w;
|
||||
c.Uv = uvs;
|
||||
c.Tex = tex;
|
||||
c.Size = fvec2(w, h);
|
||||
c.Offset = baseline + yo;
|
||||
c.Cp = ii;
|
||||
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
int map_pos = ((static_cast<int>(off.y) + y) * texszs +
|
||||
(static_cast<int>(off.x) + 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];
|
||||
}
|
||||
}
|
||||
|
||||
empty = false;
|
||||
pCodeMap[ii] = c;
|
||||
free(bitmap);
|
||||
|
||||
// offset by 1 (prevents visual glitches i had)
|
||||
off.x += w + 1;
|
||||
}
|
||||
|
||||
if (!empty) {
|
||||
pMakeAtlas(true, font_tex, texszs, tex);
|
||||
}
|
||||
}
|
||||
|
||||
void Iron::Font::pMakeAtlas(bool final, vec<uc> &font_tex, int texszs,
|
||||
Texture::Ref tex) {
|
||||
tex->Load(font_tex, texszs, texszs);
|
||||
Textures.push_back(tex);
|
||||
}
|
||||
|
||||
Iron::Font::Codepoint& Iron::Font::GetCodepoint(ui cp) {
|
||||
Iron::Font::Codepoint &Iron::Font::GetCodepoint(ui cp) {
|
||||
// Check if codepoijt exist or return a static invalid one
|
||||
auto res = pCodeMap.find(cp);
|
||||
if (res == pCodeMap.end()) {
|
||||
@@ -21,4 +146,124 @@ Iron::Font::Codepoint& Iron::Font::GetCodepoint(ui cp) {
|
||||
}
|
||||
return res->second;
|
||||
}
|
||||
|
||||
fvec2 Iron::Font::GetTextBounds(ksr text, float scale) {
|
||||
// Use wstring for exemple for german äöü
|
||||
auto wtext = std::filesystem::path(text).wstring();
|
||||
// Create a temp position and offset as [0, 0]
|
||||
fvec2 res;
|
||||
float x = 0;
|
||||
// Curent Font Scale
|
||||
float cfs = (PxFactor * scale) / (float)PxHeight;
|
||||
float lh = (float)PxHeight * cfs;
|
||||
size_t index = 0;
|
||||
for (auto &it : wtext) {
|
||||
if (it == L'\0') {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
auto cp = GetCodepoint(it);
|
||||
if (!cp.Valid && it != '\n' && it != '\t' && it != ' ') {
|
||||
continue;
|
||||
}
|
||||
switch (it) {
|
||||
case L'\n':
|
||||
res.y += lh;
|
||||
res.x = std::max(res.x, x);
|
||||
x = 0.f;
|
||||
break;
|
||||
case L'\t':
|
||||
x += 16 * cfs;
|
||||
break;
|
||||
case L' ':
|
||||
x += 4 * cfs;
|
||||
// Fall trough here to get the same result as in
|
||||
// TextCommand if/else Section
|
||||
default:
|
||||
x += cp.Size.x * cfs;
|
||||
if (index != wtext.size()) {
|
||||
x += 2 * cfs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
res.x = std::max(res.x, x);
|
||||
res.y += lh;
|
||||
return res;
|
||||
}
|
||||
|
||||
void Iron::Font::CmdTextEx(vec<Command::Ref> &cmds, const fvec2 &pos, ui color,
|
||||
float scale, const std::string &text,
|
||||
AmyTextFlags flags, const fvec2 &box) {
|
||||
fvec2 off;
|
||||
float cfs = (PxFactor * scale) / (float)PxHeight;
|
||||
float lh = (float)PxHeight * cfs;
|
||||
fvec2 td;
|
||||
fvec2 rpos = pos;
|
||||
fvec2 rbox = box;
|
||||
if (flags & (AmyTextFlags_AlignMid | AmyTextFlags_AlignRight)) {
|
||||
td = GetTextBounds(text, scale);
|
||||
}
|
||||
if (flags & AmyTextFlags_AlignMid) {
|
||||
rpos = rbox * 0.5 - td * 0.5 + pos;
|
||||
}
|
||||
if (flags & AmyTextFlags_AlignRight) {
|
||||
rpos.x = rpos.x - td.x;
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::istringstream iss(text);
|
||||
std::string tmp;
|
||||
while (std::getline(iss, tmp)) {
|
||||
lines.push_back(tmp);
|
||||
}
|
||||
|
||||
for (auto &it : lines) {
|
||||
/*if (flags & AmyTextFlags_Short) {
|
||||
fvec2 tmp_dim;
|
||||
it = ShortText(it, box.x() - pos.x(), tmp_dim);
|
||||
}*/
|
||||
// Bitte nicht nachmachen... Also ernsthaft jz, bitte macht das nicht
|
||||
auto wline = std::filesystem::path(it).wstring();
|
||||
auto cmd = Command::New();
|
||||
auto Tex = GetCodepoint(wline[0]).Tex;
|
||||
cmd->Tex = Tex;
|
||||
for (auto &jt : wline) {
|
||||
auto cp = GetCodepoint(jt);
|
||||
if ((!cp.Valid && jt != L' ' && jt != L'\n' && jt != L'\t') &&
|
||||
jt != L'\r') {
|
||||
continue;
|
||||
}
|
||||
if (Tex != cp.Tex) {
|
||||
cmds.push_back(std::move(cmd));
|
||||
cmd = Command::New();
|
||||
Tex = cp.Tex;
|
||||
cmd->Tex = Tex;
|
||||
}
|
||||
if (jt == L'\t') {
|
||||
off.x += 16 * cfs;
|
||||
} else {
|
||||
if (jt != L' ') {
|
||||
if (flags & AmyTextFlags_Shaddow) {
|
||||
// Draw
|
||||
Rect rec = Iron::PrimRect(
|
||||
rpos + vec2(off.x + 1, off.y + (cp.Offset * cfs)) + 1,
|
||||
cp.Size * cfs, 0.f);
|
||||
Iron::CmdQuad(cmd.get(), rec, cp.Uv, 0xff111111);
|
||||
}
|
||||
// Draw
|
||||
Rect rec = Iron::PrimRect(rpos + off + fvec2(0, (cp.Offset * cfs)),
|
||||
cp.Size * cfs, 0.f);
|
||||
Iron::CmdQuad(cmd.get(), rec, cp.Uv, color);
|
||||
off.x += cp.Size.x * cfs + 2 * cfs;
|
||||
} else {
|
||||
off.x += 4 * cfs;
|
||||
}
|
||||
}
|
||||
}
|
||||
cmds.push_back(std::move(cmd));
|
||||
off.y += lh;
|
||||
off.x = 0;
|
||||
}
|
||||
}
|
||||
} // namespace Amy
|
||||
@@ -51,7 +51,7 @@ int Iron::uLocProj = 0;
|
||||
C3D::Shader* Iron::m_shader = nullptr;
|
||||
mat4 Iron::m_mtx;
|
||||
int Iron::m_idx = 0, Iron::m_vtx = 0;
|
||||
Texture* Iron::m_solid = nullptr;
|
||||
Texture::Ref Iron::m_solid = nullptr;
|
||||
ui Iron::VertexCount = 0;
|
||||
ui Iron::IndexCount = 0;
|
||||
|
||||
@@ -63,7 +63,7 @@ void Iron::Init() {
|
||||
}
|
||||
|
||||
void Iron::Exit() {
|
||||
delete m_solid;
|
||||
m_solid.reset();
|
||||
delete m_shader;
|
||||
m_vbuf.clear();
|
||||
m_ibuf.clear();
|
||||
@@ -89,7 +89,7 @@ void Iron::Draw(const std::vector<Iron::Command::Ref>& data) {
|
||||
pFragConfig();
|
||||
size_t i = 0;
|
||||
while (i < data.size()) {
|
||||
Texture* tex = data[i]->Tex;
|
||||
Texture::Ref tex = data[i]->Tex;
|
||||
if (!tex) {
|
||||
i++;
|
||||
continue;
|
||||
@@ -157,7 +157,7 @@ void Iron::pFragConfig() {
|
||||
void Iron::pInitSolidTex() {
|
||||
// i know there is a lot of memory wasted :(
|
||||
std::vector<uc> pixels(16 * 16 * 4, 0xff);
|
||||
m_solid = new Texture();
|
||||
m_solid = Texture::New();
|
||||
m_solid->Load(pixels, 16, 16);
|
||||
if (!m_solid->Ptr()) {
|
||||
throw Error("white tex failed to load!");
|
||||
@@ -239,14 +239,15 @@ void Iron::CmdTriangle(Command* cmd, const fvec2& a, const fvec2& b,
|
||||
}
|
||||
|
||||
void Iron::CmdConvexPolyFilled(Command* cmd, const std::vector<fvec2>& points,
|
||||
ui color, Texture* tex) {
|
||||
ui color, Texture::Ref tex) {
|
||||
if (points.size() < 3 || tex == nullptr) {
|
||||
#ifdef AMY_GOD_DEV
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("[amy] iron: trying to render convex poly with " +
|
||||
std::to_string(points.size()) +
|
||||
" points and Texture " + std::to_string((ui)tex));
|
||||
" points and Texture " +
|
||||
std::to_string((ui)tex.get()));
|
||||
#endif
|
||||
}
|
||||
// Support for Custom Textures (UV calculation)
|
||||
|
||||
@@ -70,7 +70,7 @@ ull GetTimeMilli() {
|
||||
.count();
|
||||
}
|
||||
|
||||
void String2U16(us* res, const std::string& src, size_t max) {
|
||||
void String2U16(us* res, ksr src, size_t max) {
|
||||
/// GOT FORCED TO REPLACE std::wstring_convert by some
|
||||
/// manual work as it got removed in cxx20
|
||||
/// TODO ///
|
||||
|
||||
Reference in New Issue
Block a user