Merge dev-0.7-rewrite into stable

This commit is contained in:
2026-09-06 16:23:08 +02:00
216 changed files with 34054 additions and 53359 deletions
+34
View File
@@ -0,0 +1,34 @@
#include <iostream>
#include <pd/common.hpp>
namespace PD {
constexpr const char* pColorNo = "\033[0m";
constexpr const char* pColorYellow = "\033[33m";
constexpr const char* pColorRed = "\033[31m";
static LogLevel pFilter = LogLevel::Info;
PD_API void LogFilter(LogLevel lvl) { pFilter = lvl; }
PD_API void Log(const std::string& txt, LogLevel lvl) {
if ((int)lvl < (int)pFilter) return;
const char* clr = pColorNo;
const char* plvl = "INFO";
switch (lvl) {
case PD::LogLevel::None:
case PD::LogLevel::Info:
clr = pColorNo;
plvl = "INFO";
break;
case PD::LogLevel::Warning:
clr = pColorYellow;
plvl = "WARNING";
break;
case PD::LogLevel::Error:
clr = pColorRed;
plvl = "ERROR";
break;
}
std::cout << clr << "[PD][" << plvl << "] " << txt << pColorNo << std::endl;
}
} // namespace PD
+38 -38
View File
@@ -1,38 +1,38 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/bit_util.hpp>
namespace PD::BitUtil {
PD_API bool IsSingleBit(u32 v) { return v && !(v & (v - 1)); }
PD_API u32 GetPow2(u32 v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return (v >= 64 ? v : 64);
}
} // namespace PD::BitUtil
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/bits.hpp>
namespace PD::Bits {
PD_API bool IsSingleBit(u32 v) { return v && !(v & (v - 1)); }
PD_API u32 GetPow2(u32 v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return (v >= 64 ? v : 64);
}
} // namespace PD::Bits
+3 -7
View File
@@ -26,14 +26,10 @@ SOFTWARE.
namespace PD {
PD_API std::string Color::Hex(bool rgba) const {
/** Need to int cast (so it is used as num and not char...) */
std::stringstream s;
s << "#";
s << std::hex << std::setw(2) << std::setfill('0') << (int)r;
s << std::hex << std::setw(2) << std::setfill('0') << (int)g;
s << std::hex << std::setw(2) << std::setfill('0') << (int)b;
std::string ret = std::format("#{:02X}{:02X}{:02X}", r, g, b);
if (rgba || a != 255) { // QoL change btw
s << std::hex << std::setw(2) << std::setfill('0') << (int)a;
ret += std::format("{:02X}", a);
}
return s.str();
return ret;
}
} // namespace PD
+7 -4
View File
@@ -22,22 +22,25 @@ SOFTWARE.
*/
#include <pd/core/timer.hpp>
#include <pd/drivers/drivers.hpp>
namespace PD {
PD_API Timer::Timer(OsDriver& os, bool autostart) : pOs(os) {
PD_API Timer::Timer(bool autostart) {
pIsRunning = autostart;
Reset();
}
PD_API void Timer::Reset() {
pStart = pOs.GetTime();
pStart = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
pNow = pStart;
}
PD_API void Timer::Update() {
if (pIsRunning) {
pNow = pOs.GetTime();
pNow = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
}
+21 -7
View File
@@ -22,16 +22,30 @@ SOFTWARE.
*/
#include <pd/core/timetrace.hpp>
#include <pd/drivers/drivers.hpp>
#include <pd/drivers/os.hpp>
namespace PD::TT {
PD_API void Beg(OsDriver& os, const std::string& id) {
auto trace = os.GetTraceRef(id);
trace->SetStart(os.GetNanoTime());
static TraceMap pTraces;
PD_API TraceMap& GetTraceMap() { return pTraces; }
PD_API TT::Res& GetTraceRef(const std::string& id) {
if (!pTraces.count(id)) {
pTraces[id] = TT::Res();
pTraces[id].SetID(id);
}
return pTraces[id];
}
PD_API void End(OsDriver& os, const std::string& id) {
auto trace = os.GetTraceRef(id);
trace->SetEnd(os.GetNanoTime());
PD_API bool TraceExist(const std::string& id) { return pTraces.count(id); }
PD_API void Beg(const std::string& id) {
auto& trace = GetTraceRef(id);
trace.SetStart(PD::Os::GetTimeNano());
}
PD_API void End(const std::string& id) {
auto& trace = GetTraceRef(id);
trace.SetEnd(PD::Os::GetTimeNano());
}
} // namespace PD::TT
-42
View File
@@ -1,42 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/drivers/context.hpp>
namespace PD {
PD_API Context::Ref Context::Create() {
Context::Ref ctx = Context::New();
ctx->pGfx = GfxDriver::New();
ctx->pOs = OsDriver::New();
ctx->pHid = HidDriver::New();
return ctx;
}
PD_API PD::Li::Texture::Ref Context::GetSolidTex() {
if (pSolidTex == nullptr) {
std::vector<u8> data(16 * 16 * 4, 0xff);
pSolidTex = pGfx->LoadTex(data, 16, 16);
}
return pSolidTex;
}
} // namespace PD
Executable → Regular
+50 -24
View File
@@ -1,26 +1,52 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/drivers/gfx.hpp>
#include <pd/lithium/formatters.hpp>
namespace PD {} // namespace PD
namespace PD {
PD_API std::unique_ptr<GfxDriver> Gfx::driver;
PD_API GfxDriver::GfxDriver(std::string_view name) : DriverInterface(name) {}
PD_API GfxDriver::~GfxDriver() {
if (pTextureRegestry.size()) {
PDERR("GfxDriver: {} is still holding {} texture{}!", GetName(),
pTextureRegestry.size(), (pTextureRegestry.size() == 1 ? "" : "s"));
}
}
PD_API void GfxDriver::SetViewPort(const ivec2& size) {
ViewPort = size;
Projection = Mat4::Ortho(0.f, ViewPort.x, ViewPort.y, 0.f, 1.f, -1.f);
}
PD_API void GfxDriver::SetViewPort(int x, int y) {
ViewPort.x = x;
ViewPort.y = y;
Projection = Mat4::Ortho(0.f, ViewPort.x, ViewPort.y, 0.f, 1.f, -1.f);
}
PD_API void GfxDriver::Reset() {
CountIndices = CurrentIndex;
CountVertices = CurrentVertex;
CurrentVertex = 0;
CurrentIndex = 0;
CountCommands = pCountCommands;
CountDrawcalls = pCountDrawcalls;
pCountCommands = 0;
pCountDrawcalls = 0;
SysReset();
}
PD_API void GfxDriver::RegisterTexture(const Li::Texture& tex) {
pTextureRegestry[tex.GetID()] = tex;
}
PD_API void GfxDriver::UnregisterTexture(const Li::Texture& tex) {
if (pTextureRegestry.count(tex.GetID())) {
pTextureRegestry.erase(pTextureRegestry.find(tex.GetID()));
PDLOG("GfxDriver: Texture {{ {} }} has been deleted!", tex);
} else {
PDWARN("GfxDriver: WARNING Texture {{ {} }} does not exist in regestry!",
tex);
}
}
} // namespace PD
Executable → Regular
+47 -61
View File
@@ -1,62 +1,48 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/drivers/hid.hpp>
namespace PD {
PD_API bool HidDriver::IsEvent(Event e, Key keys) {
return KeyEvents[0][e] & keys;
}
PD_API bool HidDriver::IsEvent(Event e, KbKey keys) {
return KbKeyEvents[0][e].Has(keys);
}
PD_API void HidDriver::SwapTab() {
auto tkd = KeyEvents[1][Event_Down];
auto tkh = KeyEvents[1][Event_Held];
auto tku = KeyEvents[1][Event_Up];
KeyEvents[1][Event_Down] = KeyEvents[0][Event_Down];
KeyEvents[1][Event_Held] = KeyEvents[0][Event_Held];
KeyEvents[1][Event_Up] = KeyEvents[0][Event_Up];
KeyEvents[0][Event_Down] = tkd;
KeyEvents[0][Event_Held] = tkh;
KeyEvents[0][Event_Up] = tku;
}
/**
* If this func has no verride, still clear the stats
* cause if they are empty this leads to a crash
*/
PD_API void HidDriver::Update() {
// Clear States
for (int i = 0; i < 2; i++) {
KeyEvents[i][Event_Down] = 0;
KeyEvents[i][Event_Held] = 0;
KeyEvents[i][Event_Up] = 0;
for (auto& it : KbKeyEvents[i]) {
it.second = Event_Null;
}
}
}
#include <pd/drivers/hid.hpp>
namespace PD {
PD_API std::unique_ptr<HidDriver> Hid::driver;
PD_API HidDriver::HidDriver(std::string_view name) : DriverInterface(name) {}
PD_API HidDriver::~HidDriver() {}
PD_API bool HidDriver::IsEvent(Event e, HidInternal::GamepadKey keys) {
return pGamepadEvents[0][e] & keys;
}
PD_API bool HidDriver::IsEvent(Event e, HidInternal::Keyboard::Key keys) {
return pKeyboardEvents[0][e].Has(keys);
}
/**
* Todo: Keyboard support
*/
PD_API void HidDriver::SwapTab() {
auto tkd = pGamepadEvents[1][Event::Down];
auto tkh = pGamepadEvents[1][Event::Held];
auto tku = pGamepadEvents[1][Event::Up];
pGamepadEvents[1][Event::Down] = pGamepadEvents[0][Event::Down];
pGamepadEvents[1][Event::Held] = pGamepadEvents[0][Event::Held];
pGamepadEvents[1][Event::Up] = pGamepadEvents[0][Event::Up];
pGamepadEvents[0][Event::Down] = tkd;
pGamepadEvents[0][Event::Held] = tkh;
pGamepadEvents[0][Event::Up] = tku;
}
/**
* If this func has no verride, still clear the stats
* cause if they are empty this leads to a crash
*/
PD_API void HidDriver::Update() {
// Clear States
for (int i = 0; i < 2; i++) {
pGamepadEvents[i][Event::Down] = 0;
pGamepadEvents[i][Event::Held] = 0;
pGamepadEvents[i][Event::Up] = 0;
for (auto& it : pKeyboardEvents[i]) {
it.second = 0; // ? why was this Event_Null
}
}
pMouse[1] = pMouse[0]; // cycle here
}
} // namespace PD
+3 -39
View File
@@ -1,51 +1,15 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/drivers/os.hpp>
namespace PD {
PD_API TT::Res::Ref& OsDriver::GetTraceRef(const std::string& id) {
if (!pTraces.count(id)) {
pTraces[id] = TT::Res::New();
pTraces[id]->SetID(id);
}
return pTraces[id];
}
PD_API std::unique_ptr<OsDriver> Os::driver = std::make_unique<OsDriver>();
PD_API TraceMap& OsDriver::GetTraceMap() { return pTraces; }
PD_API bool OsDriver::TraceExist(const std::string& id) {
return pTraces.count(id);
}
/** Standart Driver */
PD_API u64 OsDriver::GetTime() {
PD_API u64 OsDriver::GetTime() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
PD_API u64 OsDriver::GetNanoTime() {
PD_API u64 OsDriver::GetTimeNano() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
-27
View File
@@ -1,27 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#define PD_IMAGE_IMPLEMENTATION
#include <pd/external/stb_image.hpp>
#define PD_TRUETYPE_IMPLEMENTATION
#include <pd/external/stb_truetype.hpp>
@@ -1,82 +1,82 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/image/img_convert.hpp>
namespace PD::ImgConvert {
PD_API void RGB24toRGBA32(std::vector<u8>& out, const std::vector<u8>& in,
const int& w, const int& h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 3;
int dst = (y * w + x) * 4;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
out[dst + 3] = 255;
}
}
}
PD_API void RGB32toRGBA24(std::vector<u8>& out, const std::vector<u8>& in,
const int& w, const int& h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 4;
int dst = (y * w + x) * 3;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
}
}
}
PD_API void Reverse32(std::vector<u8>& buf, const int& w, const int& h) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int i = y * w + x;
u8 t0 = buf[i + 0];
u8 t1 = buf[i + 1];
buf[i + 0] = buf[i + 3];
buf[i + 1] = buf[i + 2];
buf[i + 3] = t0;
buf[i + 2] = t1;
}
}
}
PD_API void ReverseBuf(std::vector<u8>& buf, size_t bpp, int w, int h) {
std::vector<u8> cpy = buf;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int pos = (y * w + x) * bpp;
for (size_t i = 0; i < bpp; i++) {
buf[pos + bpp - 1 - i] = cpy[pos + i];
}
}
}
}
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/image/convert.hpp>
namespace PD::ImgConvert {
PD_API void RGB24toRGBA32(std::vector<u8>& out, const std::vector<u8>& in,
const int& w, const int& h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 3;
int dst = (y * w + x) * 4;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
out[dst + 3] = 255;
}
}
}
PD_API void RGB32toRGBA24(std::vector<u8>& out, const std::vector<u8>& in,
const int& w, const int& h) {
// Converts RGB24 to RGBA32
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int src = (y * w + x) * 4;
int dst = (y * w + x) * 3;
out[dst + 0] = in[src + 0];
out[dst + 1] = in[src + 1];
out[dst + 2] = in[src + 2];
}
}
}
PD_API void Reverse32(std::vector<u8>& buf, const int& w, const int& h) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int i = y * w + x;
u8 t0 = buf[i + 0];
u8 t1 = buf[i + 1];
buf[i + 0] = buf[i + 3];
buf[i + 1] = buf[i + 2];
buf[i + 3] = t0;
buf[i + 2] = t1;
}
}
}
PD_API void ReverseBuf(std::vector<u8>& buf, size_t bpp, int w, int h) {
std::vector<u8> cpy = buf;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int pos = (y * w + x) * bpp;
for (size_t i = 0; i < bpp; i++) {
buf[pos + bpp - 1 - i] = cpy[pos + i];
}
}
}
}
} // namespace PD::ImgConvert
+163 -200
View File
@@ -1,201 +1,164 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef PD_IMAGE_BUILD_SHARED
#define PD_IMAGE_IMPLEMENTATION
#endif
#include <cstring>
#include <memory>
#include <pd/external/stb_image.hpp>
#include <pd/image/image.hpp>
#include <pd/image/img_convert.hpp>
namespace PD {
PD_API void Image::Load(const std::string& path) {
u8* img = pdi_load(path.c_str(), &pWidth, &pHeight, &fmt, 4);
if (fmt == 3) {
pdi_image_free(img);
img = pdi_load(path.c_str(), &pWidth, &pHeight, &fmt, 3);
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 3));
pFmt = RGB;
pdi_image_free(img);
} else if (fmt == 4) {
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 4));
pFmt = RGBA;
pdi_image_free(img);
}
}
PD_API void Image::Load(const std::vector<u8>& buf) {
u8* img =
pdi_load_from_memory(buf.data(), buf.size(), &pWidth, &pHeight, &fmt, 4);
if (fmt == 3) {
pdi_image_free(img);
img = pdi_load_from_memory(buf.data(), buf.size(), &pWidth, &pHeight, &fmt,
3);
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 3));
pFmt = RGB;
pdi_image_free(img);
} else if (fmt == 4) {
pBuffer = std::vector<PD::u8>(img, img + (pWidth * pHeight * 4));
pdi_image_free(img);
pFmt = RGBA;
}
}
PD_API void Image::Copy(const std::vector<u8>& buf, int w, int h, int bpp) {
this->fmt = bpp;
if (buf.size() != (size_t)w * h * bpp) {
// Size Error
return;
}
this->pBuffer.resize(w * h * bpp);
for (size_t i = 0; i < this->pBuffer.size(); i++) {
pBuffer[i] = buf[i];
}
}
PD_API void Image::FlipHorizontal() {
/**
* Dont know if i am brain dead but i think this code
* should Horizpntal flip an image
* Probably this needs some optimisation like not always calling
* Fmt2Bpp and use `* 0.5` instead of `/ 2` i guess
*/
for (int i = 0; i < pWidth / 2; i++) {
for (int j = 0; j < pHeight; j++) {
int src = (j * pWidth + i) * Fmt2Bpp(pFmt);
int dst = (j * pWidth + (pWidth - 1 - i)) * Fmt2Bpp(pFmt);
for (int k = 0; k < Fmt2Bpp(pFmt); k++) {
PD::u8 tmp = pBuffer[dst + k];
pBuffer[dst + k] = pBuffer[src + k];
pBuffer[src + k] = tmp;
}
}
}
}
PD_API void Image::FlipVertical() {
/**
* Dont know if i am brain dead but i think this code
* should Vertical flip an image
* Probably this needs some optimisation like not always calling
* Fmt2Bpp and use `* 0.5` instead of `/ 2` i guess
*/
for (int i = 0; i < pWidth; i++) {
for (int j = 0; j < pHeight / 2; j++) {
int src = (j * pWidth + i) * Fmt2Bpp(pFmt);
int dst = ((pHeight - 1 - j) * pWidth + i) * Fmt2Bpp(pFmt);
for (int k = 0; k < Fmt2Bpp(pFmt); k++) {
PD::u8 tmp = pBuffer[dst + k];
pBuffer[dst + k] = pBuffer[src + k];
pBuffer[src + k] = tmp;
}
}
}
}
PD_API void Image::Convert(Image::Ref img, Image::Format dst) {
if (img->pFmt == dst) {
return;
} else if (img->pFmt == Image::RGB && dst == Image::BGR) {
ImgConvert::ReverseBuf(img->pBuffer, 3, img->pWidth, img->pHeight);
img->pFmt = BGR;
} else if (img->pFmt == Image::RGB && dst == Image::RGBA) {
std::vector<PD::u8> cpy = img->pBuffer;
img->pBuffer.resize(img->pWidth * img->pHeight * 4);
ImgConvert::RGB24toRGBA32(img->pBuffer, cpy, img->pWidth, img->pHeight);
img->pFmt = RGBA;
} else if (img->pFmt == Image::RGBA && dst == Image::RGB) {
std::vector<PD::u8> cpy = img->pBuffer;
img->pBuffer.resize(img->pWidth * img->pHeight * 3);
ImgConvert::RGB32toRGBA24(img->pBuffer, cpy, img->pWidth, img->pHeight);
img->pFmt = RGB;
} else if (img->pFmt == Image::RGBA && dst == Image::BGRA) {
for (int i = 0; i < (img->pWidth * img->pHeight * 4); i += 4) {
u8 _tmp = img->pBuffer[i + 0];
img->pBuffer[i + 0] = img->pBuffer[i + 2];
img->pBuffer[i + 2] = _tmp;
}
} else if (img->pFmt == Image::RGBA && dst == Image::RGB565) {
Convert(img, Image::RGB);
Convert(img, Image::RGB565);
} else if (img->pFmt == Image::RGB && dst == Image::RGB565) {
auto f = [](u8 r, u8 g, u8 b) -> u16 {
u16 _r = (r >> 3);
u16 _g = (g >> 2);
u16 _b = (b >> 3);
return (_r << 11) | (_g << 5) | _b;
};
std::vector<PD::u8> cpy = img->pBuffer;
img->pBuffer.resize(img->pWidth * img->pHeight * 2);
for (int y = 0; y < img->pWidth; y++) {
for (int x = 0; x < img->pHeight; x++) {
int src = (y * img->pWidth + x) * 3;
int dst = (y * img->pWidth + x) * 2;
u16 new_px = f(cpy[src + 0], cpy[src + 1], cpy[src + 2]);
img->pBuffer[dst + 0] = new_px >> 8;
img->pBuffer[dst + 1] = new_px & 0xff;
}
}
img->pFmt = RGB565;
}
}
PD_API int Image::Fmt2Bpp(Format fmt) {
switch (fmt) {
case RGBA:
case ABGR:
return 4;
break;
case RGB:
case BGR:
return 3;
break;
case RGB565:
return 2;
break;
default:
return 0;
break;
}
}
PD_API void Image::ReTile(Image::Ref img,
std::function<u32(int x, int y, int w)> src,
std::function<u32(int x, int y, int w)> dst) {
std::vector<PD::u8> cpy = img->pBuffer;
/** could use fmt here but for 565 that woulnt work as it is not supported by
* file loading where fmt is used */
int bpp = Fmt2Bpp(img->pFmt);
for (int y = 0; y < img->pHeight; y++) {
for (int x = 0; x < img->pWidth; x++) {
int src_idx = src(x, y, img->pWidth);
int dst_idx = dst(x, y, img->pWidth);
for (int i = 0; i < bpp; i++) {
img->pBuffer[dst_idx + i] = cpy[src_idx + i];
}
}
}
}
#include <pd/image/convert.hpp>
#include <pd/image/image.hpp>
#if defined(PD_INCLUDE_STB_IMAGE)
#define STB_IMAGE_IMPLEMENTATION
#endif
#include <stb_image.h>
namespace PD {
PD_API Image::Image() {}
PD_API Image::Image(const std::string& path) { Load(path); }
PD_API Image::Image(const std::vector<u8>& buf) { Load(buf); }
PD_API Image::Image(const std::vector<u8>& pixels, int w, int h, int bpp) {
Copy(pixels, w, h, bpp);
}
PD_API Image::~Image() {}
PD_API void Image::Load(const u8* buf, size_t size) {
int w = 0, h = 0, c = 0;
u8* img = stbi_load_from_memory(buf, size, &w, &h, &c, 4);
if (c == 3) {
stbi_image_free(img);
img = stbi_load_from_memory(buf, size, &w, &h, &c, 3);
pFormat = Format::RGB;
}
pData.assign(img, img + (w * h * c));
pSize = ivec2(w, h);
stbi_image_free(img);
}
PD_API void Image::Load(const std::string& path) {
int w = 0, h = 0, c = 0;
u8* img = stbi_load(path.c_str(), &w, &h, &c, 4);
if (c == 3) {
stbi_image_free(img);
img = stbi_load(path.c_str(), &w, &h, &c, 3);
pFormat = Format::RGB;
}
pData.assign(img, img + (w * h * c));
pSize = ivec2(w, h);
stbi_image_free(img);
}
PD_API void Image::Load(const std::vector<u8>& buf) {
Load(buf.data(), buf.size());
}
PD_API void Image::Copy(const std::vector<u8>& pixels, int w, int h, int bpp) {
pData = pixels;
pSize = ivec2(w, h);
pFormat = GuessFmtFromBpp(bpp);
}
PD_API void Image::Convert(Format dst) {
if (pFormat == dst) {
return;
} else if (pFormat == Format::RGB && dst == Format::BGR) {
ImgConvert::ReverseBuf(pData, 3, pSize.x, pSize.y);
pFormat = Format::BGR;
} else if (pFormat == Format::RGB && dst == Format::RGBA) {
std::vector<PD::u8> cpy = pData;
pData.resize(pSize.x * pSize.y * 4);
ImgConvert::RGB24toRGBA32(pData, cpy, pSize.x, pSize.y);
pFormat = Format::RGBA;
} else if (pFormat == Format::RGBA && dst == Format::RGB) {
std::vector<PD::u8> cpy = pData;
pData.resize(pSize.x * pSize.y * 3);
ImgConvert::RGB32toRGBA24(pData, cpy, pSize.x, pSize.y);
pFormat = Format::RGB;
} else if (pFormat == Format::RGBA && dst == Format::BGRA) {
for (int i = 0; i < (pSize.x * pSize.y * 4); i += 4) {
u8 _tmp = pData[i + 0];
pData[i + 0] = pData[i + 2];
pData[i + 2] = _tmp;
}
} else if (pFormat == Format::RGBA && dst == Format::RGB565) {
Convert(Format::RGB);
Convert(Format::RGB565);
} else if (pFormat == Format::RGB && dst == Format::RGB565) {
auto f = [](u8 r, u8 g, u8 b) -> u16 {
u16 _r = (r >> 3);
u16 _g = (g >> 2);
u16 _b = (b >> 3);
return (_r << 11) | (_g << 5) | _b;
};
std::vector<PD::u8> cpy = pData;
pData.resize(pSize.x * pSize.y * 2);
for (int y = 0; y < pSize.x; y++) {
for (int x = 0; x < pSize.y; x++) {
int src = (y * pSize.x + x) * 3;
int dst = (y * pSize.x + x) * 2;
u16 new_px = f(cpy[src + 0], cpy[src + 1], cpy[src + 2]);
pData[dst + 0] = new_px >> 8;
pData[dst + 1] = new_px & 0xff;
}
}
pFormat = Format::RGB565;
}
}
PD_API int Image::Format2Bpp(Format fmt) {
switch (fmt) {
case Format::RGBA:
case Format::ABGR:
case Format::BGRA:
return 4;
case Format::BGR:
case Format::RGB:
return 3;
case Format::RGB565:
return 2;
}
return 0;
}
PD_API Image::Format Image::GuessFmtFromBpp(int bpp) {
/** Only return defaults here */
switch (bpp) {
case 4:
return Format::RGBA;
case 3:
return Format::RGB;
case 2:
return Format::RGB565;
default:
return Format::RGBA;
}
}
PD_API void Image::Flip(bool hz, bool vt) {
auto bpp = Format2Bpp(pFormat);
int rlen = pSize.x * bpp; // calculate as less as possible
if (hz) {
for (int j = 0; j < pSize.y; j++) {
int roff = j * rlen;
for (int i = 0; i < pSize.x / 2; i++) {
int src = roff + (i * bpp);
int dst = roff + (pSize.x - 1 - i) * bpp;
for (int k = 0; k < bpp; k++) {
PD::u8 tmp = pData[dst + k];
pData[dst + k] = pData[src + k];
pData[src + k] = tmp;
}
}
}
}
if (vt) {
for (int j = 0; j < pSize.y / 2; j++) {
int rsrc = j * rlen;
int rdst = (pSize.y - 1 - j) * rlen;
for (int i = 0; i < rlen; i++) { // swap the entire row
PD::u8 tmp = pData[rdst + i];
pData[rdst + i] = pData[rsrc + i];
pData[rsrc + i] = tmp;
}
}
}
}
} // namespace PD
-87
View File
@@ -1,87 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <cstring>
#include <memory>
#include <pd/image/img_blur.hpp>
#include <pd/image/img_convert.hpp>
namespace PD {
namespace ImgBlur {
PD_API std::vector<float> GaussianKernel(int r, float si) {
/// Define radius as r to be shorter
int size = 2 * r + 1;
std::vector<float> kernel(size);
float sum = 0.0f;
for (int i = -r; i <= r; i++) {
kernel[i + r] = exp(-0.5f * (i * i) / (si * si));
sum += kernel[i + r];
}
for (int i = 0; i < size; i++) {
kernel[i] /= sum;
}
return kernel;
}
PD_API void GaussianBlur(std::vector<u8>& buf, int w, int h, float radius,
float si, std::function<int(int, int, int)> idxfn) {
GaussianBlur(buf.data(), w, h, 4, radius, si, idxfn);
}
PD_API void GaussianBlur(void* buf, int w, int h, int bpp, float radius,
float si, std::function<int(int, int, int)> idxfn) {
if (bpp != 4 && bpp != 3) {
return;
}
std::vector<float> kernel = GaussianKernel(radius, si);
int hks = kernel.size() / 2;
int end = w * h * bpp;
std::vector<unsigned char> res((u8*)buf, ((u8*)buf) + end);
ImgConvert::Reverse32(res, w, h);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
float r = 0.0f, g = 0.0f, b = 0.0f;
for (int ky = -hks; ky <= hks; ky++) {
for (int kx = -hks; kx <= hks; kx++) {
int xoff = std::min(std::max(x + kx, 0), w - 1);
int yoff = std::min(std::max(y + ky, 0), h - 1);
int idx = idxfn(xoff, yoff, w) * 4;
float weight = kernel[ky + hks] * kernel[kx + hks];
r += ((u8*)buf)[idx] * weight;
g += ((u8*)buf)[idx + 1] * weight;
b += ((u8*)buf)[idx + 2] * weight;
}
}
int idx = idxfn(x, y, w) * bpp;
res[idx] = std::min(std::max(int(r), 0), 255);
res[idx + 1] = std::min(std::max(int(g), 0), 255);
res[idx + 2] = std::min(std::max(int(b), 0), 255);
}
}
ImgConvert::Reverse32(res, w, h);
std::memcpy(buf, res.data(), res.size());
}
} // namespace ImgBlur
} // namespace PD
+82 -97
View File
@@ -1,102 +1,87 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/drivers/gfx.hpp>
#include <pd/lithium/command.hpp>
#include <pd/lithium/pools.hpp>
PD_API PD::Li::Command::Ref PD::Li::CmdPool::NewCmd() {
if (pPoolIdx >= pPool.size()) {
Resize(pPool.size() + 128);
}
Command::Ref nu = pPool[pPoolIdx++];
nu->Layer = Layer;
nu->Index = pPoolIdx - 1;
return nu;
}
namespace PD {
namespace Li {
PD_API void PD::Li::CmdPool::Init(size_t initial_size) { Resize(initial_size); }
PD_API void PD::Li::CmdPool::Deinit() {
for (auto it : pPool) {
Command::Delete(it);
}
pPool.clear();
}
PD_API void PD::Li::CmdPool::Resize(size_t nulen) {
if (nulen <= pPool.size()) {
return; // no idea yet
}
size_t oldlen = pPool.size();
pPool.resize(nulen);
for (size_t i = oldlen; i < pPool.size(); i++) {
pPool[i] = Command::New();
}
}
PD_API void PD::Li::CmdPool::Reset() {
for (u32 i = 0; i < pPoolIdx; i++) {
pPool[i]->Clear();
}
pPoolIdx = 0;
}
PD::Li::Command::Ref PD::Li::CmdPool::GetCmd(size_t idx) const {
return pPool[idx];
}
PD::Li::Command::Ref PD::Li::CmdPool::GetCmd(size_t idx) { return pPool[idx]; }
size_t PD::Li::CmdPool::Size() const { return pPoolIdx; }
size_t PD::Li::CmdPool::Cap() const { return pPool.size(); }
PD_API void PD::Li::CmdPool::Merge(CmdPool& p) {
Copy(p);
p.Reset();
}
PD_API void PD::Li::CmdPool::Copy(CmdPool& p) {
if (pPoolIdx + p.Size() > pPool.size()) {
Resize(pPoolIdx + p.Size());
}
for (size_t i = 0; i < p.Size(); i++) {
size_t idx = pPoolIdx++;
*pPool[idx] = *p.GetCmd(i);
pPool[idx]->Index = idx;
pPool[idx]->Layer += Layer;
}
}
PD_API void PD::Li::CmdPool::Sort() {
if (pPoolIdx < 2) return;
std::sort(begin(), end(), pTheOrder);
}
PD_API bool PD::Li::CmdPool::pTheOrder(const Command::Ref& a,
const Command::Ref& b) {
if (a->Layer == b->Layer) {
if (a->Tex == b->Tex) {
return a->Index < b->Index;
void Command::Reserve(size_t vtx, size_t idx) {
auto& vpool = GetVertexPool();
auto& ipool = GetIndexPool();
if (VertexCountMax == 0) {
FirstVertex = vpool.size();
vpool.Allocate(vtx);
VertexCountMax = vtx;
} else {
if (vpool.size() == FirstVertex + VertexCountMax) {
vpool.Allocate(vtx);
VertexCountMax += vtx;
} else {
size_t tmp = FirstVertex;
FirstVertex = vpool.size();
vpool.Allocate(VertexCountMax + vtx);
for (size_t i = 0; i < VertexCount; i++) {
vpool[FirstVertex + i] = vpool[tmp + i];
}
VertexCountMax += vtx;
}
return a->Tex < b->Tex;
}
return a->Layer < b->Layer;
}
if (IndexCountMax == 0) {
FirstIndex = ipool.size();
ipool.Allocate(idx);
IndexCountMax = idx;
} else {
if (ipool.size() == FirstIndex + IndexCountMax) {
ipool.Allocate(idx);
IndexCountMax += idx;
} else {
size_t tmp = FirstIndex;
FirstIndex = ipool.size();
ipool.Allocate(IndexCountMax + idx);
for (size_t i = 0; i < IndexCount; i++) {
ipool[FirstIndex + i] = ipool[tmp + i];
}
IndexCountMax += idx;
}
}
}
void Command::Reset() {
Layer = 0;
Tex = 0;
SDF = false;
FirstIndex = 0;
FirstVertex = 0;
IndexCount = 0;
VertexCount = 0;
VertexCountMax = 0;
IndexCountMax = 0;
}
Command& Command::Add(const Vertex& vtx) {
if (VertexCount < VertexCountMax) {
GetVertexPool()[FirstVertex + VertexCount++] = vtx;
}
return *this;
}
Command& Command::Add(u16 idx) {
if (IndexCount < IndexCountMax) {
GetIndexPool()[FirstIndex + IndexCount++] =
static_cast<u16>(VertexCount + idx);
}
return *this;
}
Command& Command::Add(u16 a, u16 b, u16 c) {
if (IndexCount + 3 <= IndexCountMax) {
auto& ip = GetIndexPool();
size_t idx = FirstIndex + IndexCount;
ip[idx + 0] = static_cast<u16>(VertexCount + a);
ip[idx + 1] = static_cast<u16>(VertexCount + b);
ip[idx + 2] = static_cast<u16>(VertexCount + c);
IndexCount += 3;
}
return *this;
}
} // namespace Li
} // namespace PD
+392 -314
View File
@@ -1,314 +1,392 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/lithium/drawlist.hpp>
#include <pd/lithium/renderer.hpp>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace PD {
namespace Li {
PD_API DrawList::DrawList(Context& ctx, int initial_size) : pCtx(&ctx) {
DrawSolid();
pPool.Init(initial_size);
}
PD_API DrawList::~DrawList() {
Clear();
pPool.Deinit();
}
PD_API void DrawList::DrawSolid() { CurrentTex = pCtx->GetSolidTex(); }
PD_API void DrawList::Clear() {
pNumIndices = 0;
pNumVertices = 0;
pPool.Reset();
pPath.clear();
if (pCurrentFont) {
pCurrentFont->CleanupTMS();
}
while (!pClipRects.empty()) {
pClipRects.pop();
}
DrawSolid();
}
PD_API void DrawList::Merge(DrawList::Ref list) {
pPool.Merge(list->pPool);
/*for (size_t i = 0; i < list->pDrawList.size(); i++) {
pNumIndices += list->pDrawList[i]->IndexBuffer.size();
pNumVertices += list->pDrawList[i]->VertexBuffer.size();
auto cmd = pPool.NewCmd();
pDrawList.push_back(list->pDrawList[i]);
}*/
/** Make sure The list gets cleared */
list->Clear();
}
PD_API void DrawList::Copy(DrawList::Ref list) { pPool.Copy(list->pPool); }
PD_API void DrawList::Optimize() {
pPool.Sort();
/*std::sort(pDrawList.begin(), pDrawList.end(),
[](const PD::Li::Command::Ref &a, const PD::Li::Command::Ref &b) {
if (a->Layer == b->Layer) { // Same layer
if (a->Tex == b->Tex) { // same tex
return a->Index < b->Index;
}
return a->Tex < b->Tex; // order by address
}
return a->Layer < b->Layer; // Order by layer
});*/
}
PD_API Command::Ref DrawList::GetNewCmd() {
Command::Ref cmd = pPool.NewCmd();
cmd->Index = pPool.Size() - 1;
cmd->Tex = CurrentTex->Address;
pClipCmd(cmd);
return cmd;
}
PD_API void DrawList::pClipCmd(Command::Ref cmd) {
if (!pClipRects.empty()) {
cmd->ScissorOn = true;
cmd->ScissorRect = ivec4(pClipRects.top());
}
}
PD_API void DrawList::PathArcToN(const fvec2& c, float radius, float a_min,
float a_max, int segments) {
// Path.push_back(c);
PathReserve(segments + 1);
for (int i = 0; i < segments; i++) {
float a = a_min + ((float)i / (float)segments) * (a_max - a_min);
PathAdd(vec2(c.x + std::cos(a) * radius, c.y + std::sin(a) * radius));
}
}
PD_API void DrawList::PathFastArcToN(const fvec2& c, float r, float amin,
float amax, int s) {
/**
* Funcion with less division overhead
* Usefull for stuff where a lot of calculations are required
*/
float d = (amax - amin) / s;
PathReserve(s + 1);
for (int i = 0; i <= s; i++) {
float a = amin + i * d;
PathAdd(fvec2(c.x + std::cos(a) * r, c.y + std::sin(a) * r));
}
}
PD_API void DrawList::PathRect(fvec2 a, fvec2 b, float rounding) {
if (rounding == 0.f) {
PathAdd(a);
PathAdd(vec2(b.x, a.y));
PathAdd(b);
PathAdd(vec2(a.x, b.y));
} else {
float r = std::min({rounding, (b.x - a.x) * 0.5f, (b.y - a.y) * 0.5f});
/** Calculate Optimal segment count automatically */
float corner = M_PI * 0.5f;
int segments = std::max(3, int(std::ceil(corner / (6.0f * M_PI / 180.0f))));
/**
* To Correctly render filled shapes with Paths API
* The Commands need to be setup clockwise
*/
/** Top Left */
PathAdd(vec2(a.x + r, a.y));
PathFastArcToN(vec2(b.x - r, a.y + r), r, -M_PI / 2.0f, 0.0f, segments);
/** Top Right */
PathAdd(vec2(b.x, b.y - r));
PathFastArcToN(vec2(b.x - r, b.y - r), r, 0.0f, M_PI / 2.0f, segments);
/** Bottom Right */
PathAdd(vec2(a.x + r, b.y));
PathFastArcToN(vec2(a.x + r, b.y - r), r, M_PI / 2.0f, M_PI, segments);
/** Bottom Left */
PathAdd(vec2(a.x, a.y + r));
PathFastArcToN(vec2(a.x + r, a.y + r), r, M_PI, 3.0f * M_PI / 2.0f,
segments);
}
}
PD_API void DrawList::PathRectEx(fvec2 a, fvec2 b, float rounding, u32 flags) {
if (rounding == 0.f) {
PathAdd(a);
PathAdd(vec2(b.x, a.y));
PathAdd(b);
PathAdd(vec2(a.x, b.y));
} else {
float r = std::min({rounding, (b.x - a.x) * 0.5f, (b.y - a.y) * 0.5f});
/** Calculate Optimal segment count automatically */
float corner = M_PI * 0.5f;
int segments = std::max(3, int(std::ceil(corner / (6.0f * M_PI / 180.0f))));
/**
* To Correctly render filled shapes with Paths API
* The Commands need to be setup clockwise
*/
/** Top Left */
if (flags & LiPathRectFlags_KeepTopLeft) {
PathAdd(a);
} else {
PathAdd(vec2(a.x + r, a.y));
PathFastArcToN(vec2(b.x - r, a.y + r), r, -M_PI / 2.0f, 0.0f, segments);
}
/** Top Right */
if (flags & LiPathRectFlags_KeepTopRight) {
PathAdd(vec2(b.x, a.y));
} else {
PathAdd(vec2(b.x, b.y - r));
PathFastArcToN(vec2(b.x - r, b.y - r), r, 0.0f, M_PI / 2.0f, segments);
}
/** Bottom Right */
if (flags & LiPathRectFlags_KeepBotRight) {
PathAdd(b);
} else {
PathAdd(vec2(a.x + r, b.y));
PathFastArcToN(vec2(a.x + r, b.y - r), r, M_PI / 2.0f, M_PI, segments);
}
/** Bottom Left */
if (flags & LiPathRectFlags_KeepBotLeft) {
PathAdd(vec2(a.x, b.y));
} else {
PathAdd(vec2(a.x, a.y + r));
PathFastArcToN(vec2(a.x + r, a.y + r), r, M_PI, 3.0f * M_PI / 2.0f,
segments);
}
}
}
PD_API void DrawList::DrawRect(const fvec2& pos, const fvec2& size, u32 color,
int thickness) {
PathRect(pos, pos + size);
// Flags is currently hardcoded (1 = close)
PathStroke(color, thickness, 1);
}
void DrawList::DrawRectFilled(const fvec2& pos, const fvec2& size, u32 color) {
PathRect(pos, pos + size);
PathFill(color);
}
PD_API void DrawList::DrawTriangle(const fvec2& a, const fvec2& b,
const fvec2& c, u32 color, int thickness) {
PathAdd(a);
PathAdd(b);
PathAdd(c);
PathStroke(color, thickness, 1);
}
PD_API void DrawList::DrawTriangleFilled(const fvec2& a, const fvec2& b,
const fvec2& c, u32 color) {
PathAdd(a);
PathAdd(b);
PathAdd(c);
PathFill(color);
}
PD_API void DrawList::DrawCircle(const fvec2& center, float rad, u32 color,
int num_segments, int thickness) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(center, rad, 0.f, am, num_segments);
}
DrawSolid(); // Only Solid Color Supported
PathStroke(color, thickness, (1 << 0));
}
PD_API void DrawList::DrawCircleFilled(const fvec2& center, float rad,
u32 color, int num_segments) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(center, rad, 0.f, am, num_segments);
}
PathFill(color);
}
// TODO: Don't render OOS
PD_API void DrawList::DrawPolyLine(const std::vector<fvec2>& points, u32 clr,
u32 flags, int thickness) {
if (points.size() < 2) {
return;
}
DrawSolid();
auto cmd = GetNewCmd();
bool close = (flags & (1 << 0));
int num_points = close ? (int)points.size() : (int)points.size() - 1;
if (flags & (1 << 1)) {
// TODO: Find a way to draw less garbage looking lines
} else {
// Non antialiased lines look awful when rendering with thickness != 1
for (int i = 0; i < num_points; i++) {
int j = (i + 1) == (int)points.size() ? 0 : (i + 1);
auto line = Renderer::PrimLine(points[i], points[j], thickness);
Renderer::CmdQuad(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), clr);
}
}
}
PD_API void DrawList::DrawConvexPolyFilled(const std::vector<fvec2>& points,
u32 clr) {
if (points.size() < 3) {
return; // Need at least three points
}
auto cmd = GetNewCmd();
Renderer::CmdConvexPolyFilled(cmd, points, clr, CurrentTex);
}
PD_API void DrawList::DrawText(const fvec2& pos, const std::string& text,
u32 color) {
if (!pCurrentFont) {
return;
}
pCurrentFont->CmdTextEx(*this, pos, color, pFontScale, text);
}
PD_API void DrawList::DrawTextEx(const fvec2& p, const std::string& text,
u32 color, LiTextFlags flags,
const fvec2& box) {
if (!pCurrentFont) {
return;
}
pCurrentFont->CmdTextEx(*this, p, color, pFontScale, text, flags, box);
}
PD_API void DrawList::DrawLine(const fvec2& a, const fvec2& b, u32 color,
int t) {
PathAdd(a);
PathAdd(b);
PathStroke(color, t);
}
PD_API void DrawList::DrawTexture(Texture::Ref tex) { CurrentTex = tex; }
} // namespace Li
} // namespace PD
#include <algorithm>
#include <iostream>
#include <pd/drivers/gfx.hpp>
#include <pd/lithium/drawlist.hpp>
#include <pd/lithium/formatters.hpp>
#include <pd/lithium/math.hpp>
namespace PD {
namespace Li {
PD_API Drawlist::Drawlist() { Clear(); }
PD_API Drawlist::~Drawlist() { Clear(); }
PD_API void Drawlist::Merge(Drawlist& other) {
size_t start = pCommands.size();
pCommands.AppendMove(other.pCommands);
for (size_t i = start; i < pCommands.size(); i++) {
pCommands[i].Layer += this->pCurrentLayer;
}
other.Clear();
}
PD_API void Drawlist::Copy(Drawlist& other) {
int start = pCommands.size();
pCommands.AppendCopy(other.pCommands);
for (size_t i = start; i < pCommands.size(); i++) {
pCommands[i].Layer += this->pCurrentLayer;
}
}
PD_API void Drawlist::Optimize() {
if (pCommands.size() <= 1) return;
std::stable_sort(pCommands.begin(), pCommands.end(),
[](const Command& a, const Command& b) {
if (a.Layer != b.Layer) return a.Layer < b.Layer;
if (a.SDF != b.SDF) return a.SDF < b.SDF;
return a.Tex < b.Tex;
});
}
PD_API void Drawlist::Clear() {
UnbindTexture();
pPath.ResetFast();
pCommands.NoReset();
pCurrentLayer = 0;
}
/** Command Allocation */
PD_API Command& Drawlist::NewCommand() {
auto cmd = pCommands.Allocate(1);
cmd->Reset();
cmd->Layer = pCurrentLayer;
cmd->Tex = pCurrentTexture.GetID();
return *cmd;
}
PD_API void Drawlist::BindTexture(const Texture& tex) { pCurrentTexture = tex; }
/** Path API */
PD_API void Drawlist::PathStroke(const PD::Color& color, int t,
LiDrawFlags flags) {
DrawPolyLine(pPath, color, flags, t);
PathClear();
}
PD_API void Drawlist::PathFill(const PD::Color& color) {
DrawConvexPolyFilled(pPath, color);
PathClear();
}
PD_API void Drawlist::PathFillGradient(const PD::Color& a, const PD::Color& b,
float rad) {
DrawConvexPolyFilled(pPath, a, b, rad);
PathClear();
}
PD_API void Drawlist::PathArcToN(const fvec2& c, float r, float amin,
float amax, int s) {
// Path.push_back(c);
PathReserve(s + 1);
for (int i = 0; i < s; i++) {
float a = amin + ((float)i / (float)s) * (amax - amin);
PathAdd(vec2(c.x + std::cos(a) * r, c.y + std::sin(a) * r));
}
}
PD_API void Drawlist::PathFastArcToN(const fvec2& c, float r, float amin,
float amax, int s) {
/**
* Funcion with less division overhead
* Usefull for stuff where a lot of calculations are required
*/
float d = (amax - amin) / s;
PathReserve(s + 1);
for (int i = 0; i <= s; i++) {
float a = amin + i * d;
PathAdd(fvec2(c.x + std::cos(a) * r, c.y + std::sin(a) * r));
}
}
PD_API void Drawlist::PathRect(const fvec2& tl, const fvec2& br,
float rounding) {
if (rounding == 0.f) {
PathAdd(tl);
PathAdd(vec2(br.x, tl.y));
PathAdd(br);
PathAdd(vec2(tl.x, br.y));
} else {
float r = std::min({rounding, (br.x - tl.x) * 0.5f, (br.y - tl.y) * 0.5f});
/** Calculate Optimal segment count automatically */
float corner = M_PI * 0.5f;
int segments = std::max(3, int(std::ceil(corner / (6.0f * M_PI / 180.0f))));
/**
* To Correctly render filled shapes with Paths API
* The Commands need to be setup clockwise
*/
/** Top Left */
PathAdd(vec2(tl.x + r, tl.y));
PathFastArcToN(vec2(br.x - r, tl.y + r), r, -M_PI / 2.0f, 0.0f, segments);
/** Top Right */
PathAdd(vec2(br.x, br.y - r));
PathFastArcToN(vec2(br.x - r, br.y - r), r, 0.0f, M_PI / 2.0f, segments);
/** Bottom Right */
PathAdd(vec2(tl.x + r, br.y));
PathFastArcToN(vec2(tl.x + r, br.y - r), r, M_PI / 2.0f, M_PI, segments);
/** Bottom Left */
PathAdd(vec2(tl.x, tl.y + r));
PathFastArcToN(vec2(tl.x + r, tl.y + r), r, M_PI, 3.0f * M_PI / 2.0f,
segments);
}
}
PD_API void Drawlist::PathRectEx(const fvec2& tl, const fvec2& br,
float rounding, LiPathRectFlags flags) {
if (rounding == 0.f) {
PathAdd(tl);
PathAdd(vec2(br.x, tl.y));
PathAdd(br);
PathAdd(vec2(tl.x, br.y));
} else {
float r = std::min({rounding, (br.x - tl.x) * 0.5f, (br.y - tl.y) * 0.5f});
/** Calculate Optimal segment count automatically */
float corner = M_PI * 0.5f;
int segments = std::max(3, int(std::ceil(corner / (6.0f * M_PI / 180.0f))));
/**
* To Correctly render filled shapes with Paths API
* The Commands need to be setup clockwise
*/
/** Top Left */
if (flags & LiPathRectFlags_KeepTopLeft) {
PathAdd(tl);
} else {
PathAdd(vec2(tl.x + r, tl.y));
PathFastArcToN(vec2(br.x - r, tl.y + r), r, -M_PI / 2.0f, 0.0f, segments);
}
/** Top Right */
if (flags & LiPathRectFlags_KeepTopRight) {
PathAdd(vec2(br.x, tl.y));
} else {
PathAdd(vec2(br.x, br.y - r));
PathFastArcToN(vec2(br.x - r, br.y - r), r, 0.0f, M_PI / 2.0f, segments);
}
/** Bottom Right */
if (flags & LiPathRectFlags_KeepBotRight) {
PathAdd(br);
} else {
PathAdd(vec2(tl.x + r, br.y));
PathFastArcToN(vec2(tl.x + r, br.y - r), r, M_PI / 2.0f, M_PI, segments);
}
/** Bottom Left */
if (flags & LiPathRectFlags_KeepBotLeft) {
PathAdd(vec2(tl.x, br.y));
} else {
PathAdd(vec2(tl.x, tl.y + r));
PathFastArcToN(vec2(tl.x + r, tl.y + r), r, M_PI, 3.0f * M_PI / 2.0f,
segments);
}
}
}
/** Drawing functions */
PD_API void Drawlist::DrawRect(const fvec2& pos, const fvec2& size,
const PD::Color& color, int t) {
PathRect(pos, pos + size);
PathStroke(color, t, LiDrawFlags_Close);
}
PD_API void Drawlist::DrawRectFilled(const fvec2& pos, const fvec2& size,
const PD::Color& color) {
PathRect(pos, pos + size);
PathFill(color);
}
PD_API void Drawlist::DrawTriangle(const fvec2& a, const fvec2& b,
const fvec2& c, const PD::Color& color,
int t) {
PathAdd(a);
PathAdd(b);
PathAdd(c);
PathStroke(color, t, LiDrawFlags_Close);
}
PD_API void Drawlist::DrawTriangleFilled(const fvec2& a, const fvec2& b,
const fvec2& c,
const PD::Color& color) {
PathAdd(a);
PathAdd(b);
PathAdd(c);
PathFill(color);
}
PD_API void Drawlist::DrawCircle(const fvec2& center, float rad,
const PD::Color& color, int num_segments,
int t) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(center, rad, 0.f, am, num_segments);
}
UnbindTexture(); // Only Solid Color Supported
PathStroke(color, t, LiDrawFlags_Close);
}
PD_API void Drawlist::DrawCircleFilled(const fvec2& center, float rad,
const PD::Color& color,
int num_segments) {
if (num_segments <= 0) {
// Auto Segment
} else {
float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments;
PathArcToN(center, rad, 0.f, am, num_segments);
}
PathFill(color);
}
PD_API void Drawlist::DrawText(const fvec2& p, const char* text,
const PD::Color& color) {
if (!pFont) return;
pFont->CmdTextEx(*this, p, color, pFontScale, text);
}
PD_API void Drawlist::DrawTextEx(const fvec2& p, const char* text,
const PD::Color& color, LiTextFlags flags,
const fvec2& box) {
if (!pFont) return;
pFont->CmdTextEx(*this, p, color, pFontScale, text, flags, box);
}
PD_API void Drawlist::DrawLine(const fvec2& a, const fvec2& b,
const PD::Color& color, int thickness) {
this->PathAdd(a);
this->PathAdd(b);
this->PathStroke(color, thickness);
}
PD_API void Drawlist::DrawPolyLine(const Pool<fvec2>& points,
const PD::Color& color, LiDrawFlags flags,
int t) {
if (points.size() < 2) {
return;
}
UnbindTexture();
auto& cmd = NewCommand();
bool close = (flags & LiDrawFlags_Close);
int num_points = close ? (int)points.size() : (int)points.size() - 1;
if (flags & LiDrawFlags_AA) {
// TODO: Find a way to draw less garbage looking lines
} else {
// Non antialiased lines look awful when rendering with thickness != 1
for (int i = 0; i < num_points; i++) {
int j = (i + 1) == (int)points.size() ? 0 : (i + 1);
auto line = Math::PrimLine(points[i], points[j], t);
this->PrimQuad(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), color);
}
}
}
PD_API void Drawlist::DrawConvexPolyFilled(const Pool<fvec2>& points,
const PD::Color& color) {
if (points.size() < 3) {
return; // Need at least three points
}
// Support for Custom Textures (UV calculation)
float minX = points[0].x, minY = points[0].y;
float maxX = minX, maxY = minY;
// Check for the max and min Positions
for (const auto& it : points) {
if (it.x < minX) minX = it.x;
if (it.y < minY) minY = it.y;
if (it.x > maxX) maxX = it.x;
if (it.y > maxY) maxY = it.y;
}
// Get Short defines for UV
// (Bottom Right is not required)
auto uv_tl = pCurrentTexture.GetUV().TopLeft();
auto uv_tr = pCurrentTexture.GetUV().TopRight();
auto uv_bl = pCurrentTexture.GetUV().BotLeft();
auto& cmd = NewCommand();
cmd.Reserve(points.size(), (points.size() - 2) * 3);
// Render
for (int i = 2; i < (int)points.size(); i++) {
cmd.Add(0, i, i - 1);
}
for (int i = 0; i < (int)points.size(); i++) {
// Calculate U and V coords
float u =
uv_tl.x + ((points[i].x - minX) / (maxX - minX)) * (uv_tr.x - uv_tl.x);
float v =
uv_tl.y + ((points[i].y - minY) / (maxY - minY)) * (uv_bl.y - uv_tl.y);
cmd.Add(Vertex(points[i], fvec2(u, v), color));
}
}
PD_API void Drawlist::DrawConvexPolyFilled(const Pool<fvec2>& points,
const PD::Color& a,
const PD::Color b, float rad) {
if (points.size() < 3) {
return; // Need at least three points
}
fvec2 dir = fvec2(std::cos(rad), std::sin(rad));
// Support for Custom Textures (UV calculation)
float minX = points[0].x, minY = points[0].y;
float maxX = minX, maxY = minY;
// Check for the max and min Positions
for (const auto& it : points) {
if (it.x < minX) minX = it.x;
if (it.y < minY) minY = it.y;
if (it.x > maxX) maxX = it.x;
if (it.y > maxY) maxY = it.y;
}
// Get Short defines for UV
// (Bottom Right is not required)
auto uv_tl = pCurrentTexture.GetUV().TopLeft();
auto uv_tr = pCurrentTexture.GetUV().TopRight();
auto uv_bl = pCurrentTexture.GetUV().BotLeft();
// Gradient
float tmin = std::numeric_limits<float>::max();
float tmax = std::numeric_limits<float>::lowest();
for (const auto& p : points) {
float t = p.x * dir.x + p.y * dir.y;
if (t < tmin) tmin = t;
if (t > tmax) tmax = t;
}
// potential div0
float irange = (tmax != tmin) ? (1.0f / (tmax - tmin)) : 0.0f;
// Command building (oder so)
auto& cmd = NewCommand();
cmd.Reserve(points.size(), (points.size() - 2) * 3);
// Render
for (int i = 2; i < (int)points.size(); i++) {
cmd.Add(0, i, i - 1);
}
// Why was this for loop not used in normal Convex Poly filled???
for (auto& it : points) {
// Calculate U and V coords
float u = uv_tl.x + ((it.x - minX) / (maxX - minX)) * (uv_tr.x - uv_tl.x);
float v = uv_tl.y + ((it.y - minY) / (maxY - minY)) * (uv_bl.y - uv_tl.y);
float t = it.x * dir.x + it.y * dir.y;
cmd.Add(Vertex(it, fvec2(u, v), PD::Color(a).Lerp(b, (t - tmin) * irange)));
}
}
PD_API void Drawlist::PrimQuad(Command& cmd, const Rect& quad, const Rect& uv,
const PD::Color& color) {
cmd.Reserve(4, 6);
cmd.Add(2, 1, 0);
cmd.Add(3, 2, 0);
cmd.Add(Vertex(quad.TopLeft(), uv.TopLeft(), color));
cmd.Add(Vertex(quad.TopRight(), uv.TopRight(), color));
cmd.Add(Vertex(quad.BotRight(), uv.BotRight(), color));
cmd.Add(Vertex(quad.BotLeft(), uv.BotLeft(), color));
}
PD_API void Drawlist::PrimTriangle(Command& cmd, const fvec2& a, const fvec2& b,
const fvec2& c, const PD::Color& color) {
cmd.Reserve(3, 3);
cmd.Add(2, 1, 0);
cmd.Add(Vertex(a, vec2(0.f, 1.f), color));
cmd.Add(Vertex(b, vec2(1.f, 1.f), color));
cmd.Add(Vertex(c, vec2(1.f, 0.f), color));
}
} // namespace Li
} // namespace PD
+348 -429
View File
@@ -1,430 +1,349 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/lithium/font.hpp>
/** Due to Limitations of Shared Lib Stuff */
#ifdef PD_LITHIUM_BUILD_SHARED
#define PD_TRUETYPE_IMPLEMENTATION
#endif
#include <pd/external/stb_truetype.hpp>
#include <pd/lithium/drawlist.hpp>
#include <pd/lithium/renderer.hpp>
#ifdef PD_LI_INCLUDE_FONTS
#include <pd/lithium/fonts.hpp>
#endif
namespace PD {
namespace Li {
PD_API void Font::LoadDefaultFont(int id, int pixel_height) {
#ifdef PD_LI_INCLUDE_FONTS
if (id < pNumFonts) {
auto font = pFontData[id];
LoadTTF(std::vector<u8>(&pFontsDataRaw[font.StartOff],
&pFontsDataRaw[font.StartOff + font.Size]),
pixel_height);
}
#endif
}
PD_API void Font::LoadTTF(const std::string& path, int height) {
/**
* Just use LoadFile2Mem which looks way cleaner
* and helps not having the font loading code twice
* when adding LoadTTF with mem support
*/
TT::Scope st(*pCtx.Os().get(), "LI_LoadTTF_" + path);
auto font = PD::IO::LoadFile2Mem(path);
LoadTTF(font, height);
}
PD_API void Font::pMakeAtlas(bool final, std::vector<u8>& font_tex, int texszs,
PD::Li::Texture::Ref tex) {
auto t = pCtx.Gfx()->LoadTex(font_tex, texszs, texszs, Texture::RGBA32,
Texture::LINEAR);
tex->CopyFrom(t);
Textures.push_back(tex);
}
PD_API void Font::LoadTTF(const std::vector<u8>& data, int height) {
/**
* 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
*/
PixelHeight = height;
int texszs = PD::BitUtil::GetPow2(height * 16);
if (texszs > 1024) {
texszs = 1024; // Max size
}
pdtt_fontinfo inf;
if (!pdtt_InitFont(&inf, data.data(), 0)) {
return;
}
float scale = pdtt_ScaleForPixelHeight(&inf, PixelHeight);
int ascent, descent, lineGap;
pdtt_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 = pdtt_FindGlyphIndex(&inf, ii);
if (gi == 0) continue;
if (pdtt_IsGlyphEmpty(&inf, gi)) continue;
int w = 0, h = 0, xo = 0, yo = 0;
unsigned char* bitmap =
pdtt_GetCodepointBitmap(&inf, scale, scale, ii, &w, &h, &xo, &yo);
if (!bitmap || w <= 0 || h <= 0) {
if (bitmap) free(bitmap);
continue;
}
u32 hashed_map = IO::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.pCodepoint = ii;
CodeMap[ii] = c;
free(bitmap);
continue;
} else {
buf_cache[hashed_map] = ii;
}
// Next row
if (off.x + w > texszs) {
off.y += PixelHeight;
off.x = 0.0f;
}
// Bake cause we go out of the tex
if (off.y + PixelHeight > 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 if needed
if (pCtx.Gfx()->Flags & LiBackendFlags_FlipUV_Y) {
uvs.y = 1.f - uvs.y;
uvs.w = 1.f - uvs.w;
}
c.SimpleUV = uvs;
c.Tex = tex;
c.Size = fvec2(w, h);
c.Offset = baseline + yo;
c.pCodepoint = 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;
CodeMap[ii] = c;
free(bitmap);
// offset by 1 (prevents visual glitches i had)
off.x += w + 1;
}
if (!empty) {
pMakeAtlas(true, font_tex, texszs, tex);
}
}
PD_API Font::Codepoint& Font::GetCodepoint(u32 cp) {
// Check if codepoijt exist or return a static invalid one
auto res = CodeMap.find(cp);
if (res == CodeMap.end()) {
static Codepoint invalid;
invalid.pInvalid = true;
return invalid;
}
return res->second;
}
PD_API fvec2 Font::GetTextBounds(const std::string& text, float scale) {
u32 id = PD::FNV1A32(text);
if (pTMS.find(id) != pTMS.end()) {
pTMS[id].TimeStamp = pCtx.Os()->GetTime();
return pTMS[id].Size;
}
// Use wstring for exemple for german äöü
auto wtext = Strings::MakeWstring(text);
// Create a temp position and offset as [0, 0]
fvec2 res;
float x = 0;
// Curent Font Scale
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
float lh = (float)PixelHeight * cfs;
size_t index = 0;
for (auto& it : wtext) {
if (it == L'\0') {
break;
}
index++;
auto cp = GetCodepoint(it);
if (cp.pInvalid && 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;
pTMS[id].ID = id;
pTMS[id].Size = res;
pTMS[id].TimeStamp = pCtx.Os()->GetTime();
return res;
}
PD_API void Font::CmdTextEx(DrawList& dl, const fvec2& pos, u32 color,
float scale, const std::string& text,
LiTextFlags flags, const fvec2& box) {
fvec2 off;
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
float lh = (float)PixelHeight * cfs;
fvec2 td;
fvec2 rpos = pos;
fvec2 rbox = box;
std::string txt = text;
if (flags & LiTextFlags_Wrap) {
txt = pWrapText(txt, scale, box, td);
}
if (flags & (LiTextFlags_AlignMid | LiTextFlags_AlignRight)) {
td = GetTextBounds(text, scale);
}
if (flags & LiTextFlags_AlignMid) {
rpos = rbox * 0.5 - td * 0.5 + pos;
}
if (flags & LiTextFlags_AlignRight) {
rpos.x = rpos.x - td.x;
}
std::vector<std::string> lines;
std::istringstream iss(txt);
std::string tmp;
while (std::getline(iss, tmp)) {
lines.push_back(tmp);
}
for (auto& it : lines) {
if (flags & LiTextFlags_NoOOS) {
if (rpos.y + off.y + lh < 0) {
off.y += lh;
continue;
}
if (rpos.y + off.y > box.y && box.y != 0) {
break;
}
}
if (flags & LiTextFlags_Short) {
fvec2 tmp_dim;
it = pShortText(it, scale, box - pos, tmp_dim);
}
auto wline = Strings::MakeWstring(it);
auto cmd = dl.GetNewCmd();
auto Tex = GetCodepoint(wline[0]).Tex;
if (Tex) {
cmd->Tex = Tex->Address;
}
for (auto& jt : wline) {
auto cp = GetCodepoint(jt);
if ((cp.pInvalid && jt != L' ' && jt != L'\n' && jt != L'\t') &&
jt != L'\r') {
continue;
}
if (Tex != cp.Tex) {
cmd = dl.GetNewCmd();
Tex = cp.Tex;
if (Tex) {
cmd->Tex = Tex->Address;
}
}
if (jt == L'\t') {
off.x += 16 * cfs;
} else {
if (jt != L' ') {
if (flags & LiTextFlags_Shaddow) {
// Draw
Rect rec = Renderer::PrimRect(
rpos + fvec2(off.x + 1, off.y + (cp.Offset * cfs)) + 1,
cp.Size * cfs, 0.f);
Renderer::CmdQuad(cmd, rec, cp.SimpleUV, 0xff111111);
}
// Draw
Rect rec = Renderer::PrimRect(
rpos + off + fvec2(0, (cp.Offset * cfs)), cp.Size * cfs, 0.f);
Renderer::CmdQuad(cmd, rec, cp.SimpleUV, color);
off.x += cp.Size.x * cfs + 2 * cfs;
} else {
off.x += 4 * cfs;
}
}
}
off.y += lh;
off.x = 0;
}
}
PD_API std::string Font::pWrapText(const std::string& txt, float scale,
const PD::fvec2& max, PD::fvec2& dim) {
u32 id = PD::FNV1A32(txt);
if (pTMS.find(id) != pTMS.end()) {
if (pTMS[id].Text.size()) {
dim = pTMS[id].Size;
pTMS[id].TimeStamp = pCtx.Os()->GetTime();
return pTMS[id].Text;
}
}
std::string ret;
std::string line;
int lx = 0;
std::stringstream s(txt);
std::string tmp;
// Simply go over every word
while (s >> tmp) {
auto d = GetTextBounds(tmp, scale);
if (lx + d.x <= max.x) {
line += tmp + ' ';
lx += d.x;
} else {
ret += line + '\n';
line = tmp + ' ';
lx = GetTextBounds(line, scale).x;
}
}
ret += line;
dim = GetTextBounds(ret, scale);
pTMS[id].ID = id;
pTMS[id].Size = dim;
pTMS[id].Text = ret;
pTMS[id].TimeStamp = pCtx.Os()->GetTime();
return ret;
}
PD_API std::string Font::pShortText(const std::string& txt, float scale,
const PD::fvec2& max, PD::fvec2& dim) {
u32 id = PD::FNV1A32(txt);
if (pTMS.find(id) != pTMS.end()) {
if (pTMS[id].Text.size()) {
dim = pTMS[id].Size;
pTMS[id].TimeStamp = pCtx.Os()->GetTime();
return pTMS[id].Text;
}
}
auto test = GetTextBounds(txt, scale);
if (test.x < max.x) {
return txt;
}
std::string ext;
std::string ph = "(...)"; // placeholder
std::string tmp = txt;
std::string ret;
auto maxlen = max.x;
size_t ext_ = tmp.find_last_of('.');
if (ext_ != tmp.npos) {
ext = tmp.substr(ext_);
tmp = tmp.substr(0, ext_);
}
maxlen -= GetTextBounds(ext, scale).x;
maxlen -= GetTextBounds(ph, scale).x;
for (auto& it : tmp) {
if (GetTextBounds(ret, scale).x > maxlen) {
ret += ph;
ret += ext;
dim = GetTextBounds(ret, scale);
return ret;
}
ret += it;
}
pTMS[id].ID = id;
pTMS[id].Size = dim;
pTMS[id].Text = ret;
pTMS[id].TimeStamp = pCtx.Os()->GetTime();
return ret;
}
PD_API void Font::CleanupTMS() {
u64 t = pCtx.Os()->GetTime();
for (auto it = pTMS.begin(); it != pTMS.end();) {
if (t - it->second.TimeStamp > 1000) {
it = pTMS.erase(it);
} else {
it++;
}
}
}
} // namespace Li
#include <pd/core/core.hpp>
#include <pd/drivers/gfx.hpp>
#include <pd/lithium/drawlist.hpp>
#include <pd/lithium/font.hpp>
#include <pd/lithium/math.hpp>
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb_truetype.h>
#include <map>
#include <utility>
namespace PD {
namespace Li {
PD_API void Font::LoadTTF(const std::string& path, int px_height,
LiFontFlags flags) {
/**
* Just use LoadFile2Mem which looks way cleaner
* and helps not having the font loading code twice
* when adding LoadTTF with mem support
*/
PDLOG("Font: Loading {}...", path);
TT::Scope st("LI_LoadTTF_" + path);
auto font = PD::IO::LoadFile2Mem(path);
PDLOG("Font Size: {}", PD::Strings::FormatBytes(font.size()));
LoadTTF(font, px_height, flags);
}
PD_API void Font::LoadTTF(const std::vector<u8>& data, int px_height,
LiFontFlags flags) {
/**
* 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
*/
pFlags = flags;
PixelHeight = px_height;
int texszs = PD::Bits::GetPow2(px_height * 16);
if (texszs > 1024) {
texszs = 1024; // Max size
}
stbtt_fontinfo inf;
if (!stbtt_InitFont(&inf, data.data(), 0)) {
return;
}
float scale = stbtt_ScaleForPixelHeight(&inf, PixelHeight);
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
int baseline = static_cast<int>(ascent * scale);
float mono_advance = 0.f;
if (IsMonospace()) {
int a, l;
int rgi = stbtt_FindGlyphIndex(&inf, 'W');
if (rgi == 0) rgi = stbtt_FindGlyphIndex(&inf, '0');
if (rgi != 0) {
stbtt_GetGlyphHMetrics(&inf, rgi, &a, &l);
mono_advance = a * scale;
}
}
// Cache to not render same codepoint tex twice
std::map<u32, int> buf_cache;
std::vector<u8> font_tex(texszs * texszs, 0);
fvec2 off;
bool empty = true;
std::vector<std::pair<u32, u32>> ranges = {
{0x0020, 0x007E}, // ASCII
{0x00A0, 0x00FF}, // LATIN-1
};
for (auto& it : ranges) {
for (u32 ii = it.first; ii <= it.second; ii++) {
int gi = stbtt_FindGlyphIndex(&inf, ii);
if (gi == 0) continue;
int advance, lsb;
stbtt_GetGlyphHMetrics(&inf, gi, &advance, &lsb);
float sadvance = advance * scale;
float final_advance = sadvance;
float center_offset = 0.f;
if (IsMonospace() && mono_advance > 0.f) {
final_advance = mono_advance;
center_offset = (mono_advance - sadvance) * 0.5f;
}
if (stbtt_IsGlyphEmpty(&inf, gi)) {
Codepoint c;
c.AdvanceX = final_advance;
c.Size = fvec2(0.f);
c.pCodepoint = ii;
c.pInvalid = false;
CodeMap[ii] = c;
continue;
}
int w = 0, h = 0, xo = 0, yo = 0;
unsigned char* bitmap = nullptr;
if (IsSDF()) {
bitmap = stbtt_GetCodepointSDF(&inf, scale, ii, 5, 128, 255.f / 5.f, &w,
&h, &xo, &yo);
} else {
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 =
IO::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.pCodepoint = ii;
CodeMap[ii] = c;
free(bitmap);
continue;
} else {
buf_cache[hashed_map] = ii;
}
// Next row
if (off.x + w > texszs) {
off.y += PixelHeight;
off.x = 0.0f;
}
// Bake cause we go out of the tex
if (off.y + PixelHeight > texszs) {
BakeAndPush(false, font_tex, texszs);
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 if needed
if (PD::Gfx::GetFlags() & PDGfxBackendFlags_FlipUV_Y) {
uvs.y = 1.f - uvs.y;
uvs.w = 1.f - uvs.w;
}
c.SimpleUV = uvs;
c.Tex = pCurrentTex;
c.Size = fvec2(w, h);
c.Offset = fvec2(xo + center_offset, baseline + yo);
c.AdvanceX = final_advance;
c.pCodepoint = 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));
font_tex[map_pos] = bitmap[x + y * w];
}
}
empty = false;
CodeMap[ii] = c;
free(bitmap);
// offset by 1 (prevents visual glitches i had)
off.x += w + 1;
}
}
if (!empty) {
BakeAndPush(true, font_tex, texszs);
}
for (u32 i = 0; i < 128; i++) {
auto r = CodeMap.find(i);
if (r == CodeMap.end()) {
static Codepoint invalid;
invalid.pInvalid = true;
pAsciiCache[i] = invalid;
} else {
pAsciiCache[i] = CodeMap[i];
}
}
}
PD_API void Font::LoadDefaultFont(int id, int pixel_height) {}
PD_API Font::Codepoint& Font::GetCodepoint(u32 c) {
if (c < 128) {
// Direct Access (~11% improvement)
return pAsciiCache[c];
}
// Check if codepoijt exist or return a static invalid one
auto res = CodeMap.find(c);
if (res == CodeMap.end()) {
static Codepoint invalid;
invalid.pInvalid = true;
return invalid;
}
return res->second;
}
PD_API fvec2 Font::GetTextBounds(const char* text, float scale) {
// Create a temp position and offset as [0, 0]
fvec2 res;
float x = 0;
// Curent Font Scale
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
float lh = (float)PixelHeight * cfs;
U8Iterator it(text);
u32 c;
while (it.Decode32(c)) {
auto cp = GetCodepoint(c);
if ((cp.pInvalid && c != L'\n' && c != L'\t' && c != L' ') && c != L'\r')
continue;
if (c == L'\n') {
res.y += lh;
res.x = std::max(res.x, x);
x = 0.f;
continue;
}
if (c == L'\t') {
x += (cp.AdvanceX > 0 ? cp.AdvanceX : 16.f) * 4.f * cfs;
continue;
}
x += cp.AdvanceX * cfs;
}
res.x = std::max(res.x, x);
res.y += lh;
return res;
}
PD_API void PrimTextQuad(Command& cmd, float x, float y, float w, float h,
const fvec4& uv, const PD::Color& color) {
cmd.Reserve(4, 6);
cmd.Add(2, 1, 0);
cmd.Add(3, 2, 0);
cmd.Add(Vertex(x, y, uv.x, uv.y, color));
cmd.Add(Vertex(x + w, y, uv.z, uv.y, color));
cmd.Add(Vertex(x + w, y + h, uv.z, uv.w, color));
cmd.Add(Vertex(x, y + h, uv.x, uv.w, color));
}
PD_API void Font::CmdTextEx(Drawlist& dl, const fvec2& pos, u32 color,
float scale, const char* text, LiTextFlags flags,
const fvec2& box) {
fvec2 off;
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
float lh = (float)PixelHeight * cfs;
fvec2 td;
fvec2 rpos = pos;
fvec2 rbox = box;
if (flags & (LiTextFlags_AlignMid | LiTextFlags_AlignRight)) {
td = GetTextBounds(text, scale);
}
if (flags & LiTextFlags_AlignMid) rpos = rbox * 0.5 - td * 0.5 + pos;
if (flags & LiTextFlags_AlignRight) rpos.x -= td.x;
U8Iterator it(text);
u32 c;
Command* cmd = dl.HasCommands() ? &dl.GetLastCommand() : nullptr;
while (it.Decode32(c)) {
auto cp = GetCodepoint(c);
if ((cp.pInvalid && c != L'\n' && c != L'\t' && c != L' ') && c != L'\r')
continue;
if (c == L'\n') {
off.y += lh;
off.x = 0.f;
continue;
}
if (c == L'\t') {
off.x += (cp.AdvanceX > 0 ? cp.AdvanceX : 16.f) * 4.f * cfs;
continue;
}
if (cp.Size.x > 0 && cp.Size.y > 0) {
if (cmd == nullptr || cmd->Tex != Textures[cp.Tex] ||
cmd->Layer != dl.GetLayer()) {
if (cp.Tex >= Textures.size()) continue;
cmd = &dl.NewCommand();
cmd->Tex = Textures[cp.Tex];
cmd->SDF = IsSDF();
}
// calculating once and using PrimTextQuad to directly push
// saves ~42% on raw multiline text draw time 6.3 -> 3.7 ms
// and ~25% on the whole frametime 20.3 -> 15.2 ms
// tested with Craftus-Next 0.8.0 commit:
// 33298ccc276bf996d341710e69ecf05f99c57961
float cx = rpos.x + off.x + (cp.Offset.x * cfs);
float cy = rpos.y + off.y + (cp.Offset.y * cfs);
float cw = cp.Size.x * cfs;
float ch = cp.Size.y * cfs;
if (flags & LiTextFlags_Shaddow) {
PrimTextQuad(*cmd, cx + 1.f, cy + 1.f, cw, ch, cp.SimpleUV, 0xff111111);
}
PrimTextQuad(*cmd, cx, cy, cw, ch, cp.SimpleUV, color);
}
off.x += cp.AdvanceX * cfs;
}
}
PD_API void Font::CleanupTMS() {}
PD_API void Font::BakeAndPush(bool final, std::vector<u8>& font_tex,
int texszs) {
auto t = PD::Gfx::LoadTexture(font_tex, texszs, texszs, TextureFormat::A8);
PDLOG("Font: Texture backed as 0x{:X} at {}", t.GetID(), pCurrentTex);
Textures.push_back(t.GetID());
pCurrentTex = Textures.size();
}
PD_API std::string Font::pWrapText(const std::string& txt, float scale,
const PD::fvec2& max, PD::fvec2& dim) {
return "";
}
PD_API std::string Font::pShortText(const std::string& txt, float scale,
const PD::fvec2& max, PD::fvec2& dim) {
return "";
}
PD_API void Font::Delete() {
for (auto& it : Textures) {
// Creating a tmp fake Li tex for deletion
PD::Gfx::DeleteTexture(PD::Li::Texture(it, 0));
}
pCurrentTex = 0;
PixelHeight = 0;
pTMS.clear();
CodeMap.clear();
}
} // namespace Li
} // namespace PD
-48
View File
@@ -1,48 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifdef PD_LI_INCLUDE_FONTS
#include <pd/lithium/fonts.hpp>
/** Generated with pdfm */
namespace PD {
FontFileData pFontData[] = {
{
"ComicNeue-Bold.ttf",
0,
1,
},
{
"Roboto-Regular.ttf",
0,
1,
},
};
size_t pNumFonts = 2;
// clang-format off
PD::u8 pFontsDataRaw[] = {
0x0
};
// clang-format on
} // namespace PD
#endif
+71
View File
@@ -0,0 +1,71 @@
#include <pd/lithium/math.hpp>
namespace PD {
namespace Li {
namespace Math {
PD_API bool InBounds(const fvec2& pos, const fvec2& size, const fvec4& rect) {
return (pos.x + size.x >= rect.x && pos.y + size.y >= rect.y &&
pos.x <= rect.z && pos.y <= rect.w);
}
PD_API bool InBounds(const fvec2& pos, const fvec4& rect) {
return (pos.x > rect.x && pos.x < rect.x + rect.z && pos.y > rect.y &&
pos.y < rect.y + rect.w);
}
PD_API bool InBounds(const fvec2& a, const fvec2& b, const fvec2& c,
const fvec4& rect) {
return ((a.x < rect.z && b.x < rect.z && c.x < rect.z) ||
(a.y < rect.w && b.y < rect.w && c.y < rect.w) ||
(a.x > 0 && b.x > 0 && c.x > 0) || (a.y > 0 && b.y > 0 && c.y > 0));
}
PD_API bool InSpace(const PD::fvec2& pos, const Rect& rect) {
return (pos.x > rect.Top.x && pos.x < rect.Top.z && pos.y > rect.Top.y &&
pos.y < rect.Bot.y);
}
PD_API void RotateCorner(fvec2& pos, float sinus, float cosinus) {
float x = pos.x * cosinus - pos.y * sinus;
float y = pos.y * cosinus - pos.x * sinus;
pos = fvec2(x, y);
}
PD_API Rect PrimRect(const fvec2& pos, const fvec2& size, float a) {
fvec2 c = size * 0.5f; // Center
fvec2 corner[4] = {
fvec2(-c.x, -c.y),
fvec2(-c.x + size.x, -c.y),
fvec2(-c.x, -c.y + size.y),
fvec2(-c.x + size.x, -c.y + size.y),
};
// Only rotate if required
if (a != 0.f) {
float s = std::sin(a);
float co = std::cos(a);
for (int i = 0; i < 4; i++) {
RotateCorner(corner[i], s, co);
}
}
// Return Result
return Rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c,
corner[3] + pos + c);
}
PD_API Rect PrimLine(const fvec2& a, const fvec2& b, int t) {
// Using the vec maths api makes the code as short as it is
vec2 dir = a - b;
float len = dir.Len();
if (len == 0.0f) return Rect();
vec2 unit_dir = dir / len;
vec2 perpendicular(-unit_dir.y, unit_dir.x);
vec2 off = perpendicular * ((float)t * 0.5f);
return Rect(a + off, b + off, a - off, b - off);
}
} // namespace Math
} // namespace Li
} // namespace PD
+16
View File
@@ -0,0 +1,16 @@
#include <pd/lithium/pools.hpp>
namespace PD {
namespace Li {
static PD::Pool<Vertex> gVertexPool;
static PD::Pool<u16> gIndexPool;
PD_API PD::Pool<Vertex>& GetVertexPool() { return gVertexPool; }
PD_API PD::Pool<u16>& GetIndexPool() { return gIndexPool; }
PD_API void ResetPools() {
gVertexPool.NoReset();
gIndexPool.NoReset();
}
} // namespace Li
} // namespace PD
-146
View File
@@ -1,146 +0,0 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/lithium/renderer.hpp>
namespace PD {
namespace Li {
PD_API bool Renderer::InBox(const fvec2& pos, const fvec2& szs,
const fvec4& rect) {
return (pos.x + szs.x >= rect.x && pos.y + szs.y >= rect.y &&
pos.x <= rect.z && pos.y <= rect.w);
}
PD_API bool Renderer::InBox(const fvec2& pos, const fvec4& rect) {
return (pos.x > rect.x && pos.x < rect.x + rect.z && pos.y > rect.y &&
pos.y < rect.y + rect.w);
}
PD_API bool Renderer::InBox(const fvec2& alpha, const fvec2& bravo,
const fvec2& charlie, const fvec4& rect) {
return ((alpha.x < rect.z && bravo.x < rect.z && charlie.x < rect.z) ||
(alpha.y < rect.w && bravo.y < rect.w && charlie.y < rect.w) ||
(alpha.x > 0 && bravo.x > 0 && charlie.x > 0) ||
(alpha.y > 0 && bravo.y > 0 && charlie.y > 0));
}
PD_API void Renderer::RotateCorner(fvec2& pos, float sinus, float cosinus) {
float x = pos.x * cosinus - pos.y * sinus;
float y = pos.y * cosinus - pos.x * sinus;
pos = fvec2(x, y);
}
PD_API Rect Renderer::PrimRect(const fvec2& pos, const fvec2& size,
float angle) {
fvec2 c = size * 0.5f; // Center
fvec2 corner[4] = {
fvec2(-c.x, -c.y),
fvec2(-c.x + size.x, -c.y),
fvec2(-c.x, -c.y + size.y),
fvec2(-c.x + size.x, -c.y + size.y),
};
// Only rotate if required
if (angle != 0.f) {
float s = std::sin(angle);
float co = std::cos(angle);
for (int i = 0; i < 4; i++) {
RotateCorner(corner[i], s, co);
}
}
// Return Result
return Rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c,
corner[3] + pos + c);
}
PD_API Rect Renderer::PrimLine(const fvec2& a, const fvec2& b, int thickness) {
// Using the vec maths api makes the code as short as it is
vec2 dir = a - b;
float len = dir.Len();
vec2 unit_dir = dir / len;
vec2 perpendicular(-unit_dir.y, unit_dir.x);
vec2 off = perpendicular * ((float)thickness * 0.5f);
return Rect(a + off, b + off, a - off, b - off);
}
PD_API void Renderer::CmdQuad(Command::Ref cmd, const Rect& quad,
const Rect& uv, u32 color) {
cmd->AddIdx(0).AddIdx(1).AddIdx(2);
cmd->AddIdx(0).AddIdx(2).AddIdx(3);
cmd->AddVtx(Vertex(quad.BotRight(), uv.BotRight(), color));
cmd->AddVtx(Vertex(quad.TopRight(), uv.TopRight(), color));
cmd->AddVtx(Vertex(quad.TopLeft(), uv.TopLeft(), color));
cmd->AddVtx(Vertex(quad.BotLeft(), uv.BotLeft(), color));
}
PD_API void Renderer::CmdTriangle(Command::Ref cmd, const fvec2 a,
const fvec2 b, const fvec2 c, u32 clr) {
cmd->AddIdx(2).AddIdx(1).AddIdx(0);
cmd->AddVtx(Vertex(a, vec2(0.f, 1.f), clr));
cmd->AddVtx(Vertex(b, vec2(1.f, 1.f), clr));
cmd->AddVtx(Vertex(c, vec2(1.f, 0.f), clr));
}
// TODO: Don't render OOS (Probably make it with a define as it
// would probably be faster to render out of screen than checking if
// it could be skipped)
PD_API void Renderer::CmdConvexPolyFilled(Command::Ref cmd,
const std::vector<fvec2>& points,
u32 clr, Texture::Ref tex) {
if (points.size() < 3 || tex == nullptr) {
return; // Need at least three points
}
// Support for Custom Textures (UV calculation)
float minX = points[0].x, minY = points[0].y;
float maxX = minX, maxY = minY;
// Check for the max and min Positions
for (const auto& it : points) {
if (it.x < minX) minX = it.x;
if (it.y < minY) minY = it.y;
if (it.x > maxX) maxX = it.x;
if (it.y > maxY) maxY = it.y;
}
// Get Short defines for UV
// (Bottom Right is not required)
auto uv_tl = tex->UV.TopLeft();
auto uv_tr = tex->UV.TopRight();
auto uv_bl = tex->UV.BotLeft();
// Render
for (int i = 2; i < (int)points.size(); i++) {
cmd->AddIdx(0).AddIdx(i).AddIdx(i - 1);
}
for (int i = 0; i < (int)points.size(); i++) {
// Calculate U and V coords
float u =
uv_tl.x + ((points[i].x - minX) / (maxX - minX)) * (uv_tr.x - uv_tl.x);
float v =
uv_tl.y + ((points[i].y - minY) / (maxY - minY)) * (uv_bl.y - uv_tl.y);
cmd->AddVtx(Vertex(points[i], fvec2(u, v), clr));
}
}
} // namespace Li
} // namespace PD
+6 -5
View File
@@ -35,8 +35,8 @@ PD_API void Button::HandleInput() {
color = UI7Color_Button;
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
if (io->InputHandler->DragObject(this->GetID(), fvec4(FinalPos(), size))) {
if (io->InputHandler->DragReleased) {
if (io->InputHandler.DragObject(this->GetID(), fvec4(FinalPos(), size))) {
if (io->InputHandler.DragReleased) {
color = UI7Color_ButtonActive;
pressed = true;
} else {
@@ -49,11 +49,12 @@ PD_API void Button::HandleInput() {
PD_API void Button::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->SetFont(GetFont());
list->PathRect(FinalPos(), FinalPos() + size, io->FrameRounding);
list->PathFill(io->Theme->Get(color));
list->PathFill(io->Theme.Get(color));
list->LayerUp();
list->DrawText(FinalPos() + size * 0.5 - tdim * 0.5, label,
io->Theme->Get(UI7Color_Text));
list->DrawText(FinalPos() + size * 0.5 - tdim * 0.5, label.c_str(),
io->Theme.Get(UI7Color_Text));
list->LayerDown();
}
+6 -5
View File
@@ -34,8 +34,8 @@ PD_API void Checkbox::HandleInput() {
/// Ensure it gets sed to false and stays if not pressed
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
if (io->InputHandler->DragObject(this->GetID(), fvec4(FinalPos(), size))) {
if (io->InputHandler->DragReleased) {
if (io->InputHandler.DragObject(this->GetID(), fvec4(FinalPos(), size))) {
if (io->InputHandler.DragReleased) {
color = UI7Color_FrameBackgroundHovered;
usr_ref = !usr_ref;
} else {
@@ -48,15 +48,16 @@ PD_API void Checkbox::HandleInput() {
PD_API void Checkbox::Draw() {
// Assert(list.get() && io.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->SetFont(GetFont());
list->PathRect(FinalPos(), FinalPos() + cbs, io->FrameRounding);
list->PathFill(io->Theme->Get(color));
list->PathFill(io->Theme.Get(color));
if (usr_ref) {
list->PathRect(FinalPos() + 2, FinalPos() + cbs - 2, io->FrameRounding);
list->PathFill(io->Theme->Get(UI7Color_Checkmark));
list->PathFill(io->Theme.Get(UI7Color_Checkmark));
}
list->DrawText(
FinalPos() + fvec2(cbs.x + io->ItemSpace.x, cbs.y * 0.5 - tdim.y * 0.5),
label, io->Theme->Get(UI7Color_Text));
label.c_str(), io->Theme.Get(UI7Color_Text));
}
PD_API void Checkbox::Update() {
+31 -26
View File
@@ -33,9 +33,8 @@ PD_API void ColorEdit::HandleInput() {
}
// Assert(screen.get(), "Screen is not set up!");
// if (screen->ScreenType() == Screen::Bottom) {
if (io->InputHandler->DragObject(this->GetID() + 2,
fvec4(FinalPos(), size))) {
if (io->InputHandler->DragReleasedAW) {
if (io->InputHandler.DragObject(this->GetID() + 2, fvec4(FinalPos(), size))) {
if (io->InputHandler.DragReleasedAW) {
is_shown = !is_shown;
}
}
@@ -45,36 +44,40 @@ PD_API void ColorEdit::HandleInput() {
PD_API void ColorEdit::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->SetFont(GetFont());
list->PathRect(FinalPos(), FinalPos() + io->ItemRowHeight, io->FrameRounding);
list->PathFill(*color_ref);
list->DrawText(FinalPos() + fvec2(io->ItemSpace.x + io->ItemRowHeight, 0),
label, io->Theme->Get(UI7Color_Text));
label.c_str(), io->Theme.Get(UI7Color_Text));
if (is_shown) {
if (!layout) {
layout = Layout::New(GetID(), io);
layout = new UI7::Layout(GetID(), *io);
}
layout->SetPosition(FinalPos());
layout->AddObjectEx(
DynObj::New(
[=, this](UI7::IO::Ref io, Li::DrawList::Ref l, Container* thiz) {
thiz->SetSize(layout->GetSize());
l->Layer(30);
l->PathRect(thiz->GetPos(), thiz->GetPos() + thiz->GetSize(),
io->FrameRounding);
l->PathFill(io->Theme->Get(UI7Color_FrameBackground));
}),
UI7LytAdd_Front | UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
auto obj = DynObj::New(
[=, this](UI7::IO::Ref io, Li::DrawList::Ref l, Container* thiz) {
l->PathRect(thiz->FinalPos(), thiz->FinalPos() + io->ItemRowHeight,
io->FrameRounding);
l->PathFill(*color_ref);
l->DrawText(
thiz->FinalPos() + fvec2(io->ItemSpace.x + io->ItemRowHeight, 0),
label, io->Theme->Get(UI7Color_Text));
});
obj->SetSize(PD::fvec2(200, io->ItemRowHeight));
layout->AddObject(obj);
DynObj* r = io->DynObjPool.Allocate();
*r = UI7::DynObj([=, this](UI7::IO* io, Li::Drawlist* l, Container* thiz) {
list->SetFont(thiz->GetFont());
thiz->SetSize(layout->GetSize());
// l->Layer(30);
l->PathRect(thiz->GetPos(), thiz->GetPos() + thiz->GetSize(),
io->FrameRounding);
l->PathFill(io->Theme.Get(UI7Color_FrameBackground));
l->SetLayer(0);
});
layout->AddObjectEx(r, UI7LytAdd_Front | UI7LytAdd_NoCursorUpdate |
UI7LytAdd_NoScrollHandle);
r = io->DynObjPool.Allocate();
*r = UI7::DynObj([=, this](UI7::IO* io, Li::Drawlist* l, Container* thiz) {
list->SetFont(thiz->GetFont());
l->PathRect(thiz->FinalPos(), thiz->FinalPos() + io->ItemRowHeight,
io->FrameRounding);
l->PathFill(*color_ref);
l->DrawText(
thiz->FinalPos() + fvec2(io->ItemSpace.x + io->ItemRowHeight, 0),
label.c_str(), io->Theme.Get(UI7Color_Text));
});
r->SetSize(PD::fvec2(200, io->ItemRowHeight));
layout->AddObject(r);
layout->Label("RGBA: ({}, {}, {}, {})", *((u8*)color_ref),
*(((u8*)color_ref) + 1), *(((u8*)color_ref) + 2),
*(((u8*)color_ref) + 3));
@@ -85,7 +88,9 @@ PD_API void ColorEdit::Draw() {
layout->Slider<u8>("B", ((u8*)color_ref) + 2);
layout->Slider<u8>("A", ((u8*)color_ref) + 3);
layout->Update();
list->SetLayer(50);
list->Merge(layout->GetDrawList());
list->SetLayer(0);
// io->RegisterDrawList(GetID(), layout->GetDrawList());
}
}
+8 -8
View File
@@ -26,15 +26,15 @@ SOFTWARE.
namespace PD {
namespace UI7 {
PD_API void Container::HandleScrolling(fvec2 scrolling, fvec4 viewport) {
if (last_use != 0 && io->pCtx.Os()->GetTime() - last_use > 5000) {
if (last_use != 0 && PD::Os::GetTime() - last_use > 5000) {
rem = true;
}
last_use = io->pCtx.Os()->GetTime();
last_use = PD::Os::GetTime();
pos -= fvec2(0, scrolling.y);
skippable = !Li::Renderer::InBox(
pos, size,
fvec4(viewport.x, viewport.y, viewport.x + viewport.z,
viewport.y + viewport.w));
skippable =
!Li::Math::InBounds(pos, size,
fvec4(viewport.x, viewport.y, viewport.x + viewport.z,
viewport.y + viewport.w));
}
PD_API void Container::HandleInternalInput() {
@@ -44,13 +44,13 @@ PD_API void Container::HandleInternalInput() {
/** Internal function */
PD_API void Container::PreDraw() {
if (pCLipRectUsed) {
list->PushClipRect(pClipRect);
// list->PushClipRect(pClipRect);
}
}
/** Internal function */
PD_API void Container::PostDraw() {
if (pCLipRectUsed) {
list->PopClipRect();
// list->PopClipRect();
}
}
} // namespace UI7
+13 -11
View File
@@ -21,6 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <algorithm>
#include <pd/ui7/container/dragdata.hpp>
#include <pd/ui7/container/label.hpp>
#include <type_traits>
@@ -52,14 +53,14 @@ PD_API void DragData<T>::HandleInput() {
} else {
p = std::format("{}", data[i]);
}
vec2 tdim = io->Font->GetTextBounds(p, io->FontScale);
vec2 tdim = io->Font->GetTextBounds(p.c_str(), io->FontScale);
// Unsafe but is the fastest solution
if (io->InputHandler->DragObject(
if (io->InputHandler.DragObject(
this->GetID() + i + 1,
fvec4(FinalPos() + fvec2(off_x, 0), tdim + io->FramePadding))) {
data[i] = std::clamp(
T(data[i] + (step * (io->InputHandler->DragPosition.x -
io->InputHandler->DragLastPosition.x))),
T(data[i] + (step * (io->InputHandler.DragPosition.x -
io->InputHandler.DragLastPosition.x))),
this->min, this->max);
}
off_x += tdim.x + io->ItemSpace.x + io->FramePadding.x;
@@ -80,26 +81,27 @@ PD_API void DragData<T>::Draw() {
} else {
p = std::format("{}", data[i]);
}
vec2 td = io->Font->GetTextBounds(p, io->FontScale);
vec2 td = io->Font->GetTextBounds(p.c_str(), io->FontScale);
list->PathRect(FinalPos() + fvec2(off_x, 0),
FinalPos() + fvec2(off_x, 0) + td + io->FramePadding,
io->FrameRounding);
list->PathFill(io->Theme->Get(UI7Color_Button));
list->PathFill(io->Theme.Get(UI7Color_Button));
list->LayerUp();
list->DrawTextEx(FinalPos() + fvec2(off_x, 0), p,
io->Theme->Get(UI7Color_Text), LiTextFlags_AlignMid,
list->DrawTextEx(FinalPos() + fvec2(off_x, 0), p.c_str(),
io->Theme.Get(UI7Color_Text), LiTextFlags_AlignMid,
td + io->FramePadding);
list->LayerDown();
off_x += td.x + io->ItemSpace.x + io->FramePadding.x;
}
list->DrawText(FinalPos() + fvec2(off_x, io->FramePadding.y * 0.5), label,
io->Theme->Get(UI7Color_Text));
list->DrawText(FinalPos() + fvec2(off_x, io->FramePadding.y * 0.5),
label.c_str(), io->Theme.Get(UI7Color_Text));
}
template <typename T>
PD_API void DragData<T>::Update() {
// Assert(io.get(), "Did you run Container::Init correctly?");
// Probably need to find a faster solution (caching sizes calculated here)
list->SetFont(GetFont());
float off_x = 0;
for (size_t i = 0; i < elm_count; i++) {
std::string p;
@@ -108,7 +110,7 @@ PD_API void DragData<T>::Update() {
} else {
p = std::format("{}", data[i]);
}
vec2 tdim = io->Font->GetTextBounds(p, io->FontScale);
vec2 tdim = io->Font->GetTextBounds(p.c_str(), io->FontScale);
off_x += tdim.x + io->ItemSpace.x + io->FramePadding.x;
}
this->SetSize(vec2(tdim.x + off_x, tdim.y + io->FramePadding.y));
+2 -3
View File
@@ -26,14 +26,13 @@ SOFTWARE.
namespace PD {
namespace UI7 {
PD_API void Image::Draw() {
if (!img) return;
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// Assert(img.get(), "Image is nullptr!");
// io->Ren->OnScreen(screen);
list->LayerUp();
list->DrawTexture(img);
list->BindTexture(img);
list->DrawRectFilled(FinalPos(), newsize, 0xffffffff);
list->DrawSolid();
list->UnbindTexture();
list->LayerDown();
}
} // namespace UI7
+5 -4
View File
@@ -28,9 +28,10 @@ namespace UI7 {
PD_API void Label::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->DrawTextEx(FinalPos(), label, io->Theme->Get(UI7Color_Text),
list->SetFont(GetFont());
list->DrawTextEx(FinalPos(), label.c_str(), io->Theme.Get(UI7Color_Text),
LiTextFlags_NoOOS,
PD::fvec2(0, io->CurrentViewPort->pSize.w));
PD::fvec2(0, io->CurrentViewPort.pSize.w));
}
PD_API void Label::Update() {
@@ -42,8 +43,8 @@ PD_API void Label::Update() {
if (io->WrapLabels) {
this->label = io->Font->pWrapText(
this->label, io->FontScale,
PD::fvec2(io->CurrentViewPort->pSize.z - FinalPos().x * 4,
io->CurrentViewPort->pSize.w),
PD::fvec2(io->CurrentViewPort.pSize.z - FinalPos().x * 4,
io->CurrentViewPort.pSize.w),
this->tdim);
SetSize(tdim);
}
+13 -11
View File
@@ -21,6 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <algorithm>
#include <pd/ui7/container/label.hpp>
#include <pd/ui7/container/slider.hpp>
#include <type_traits>
@@ -51,14 +52,14 @@ PD_API void Slider<T>::HandleInput() {
}
// Unsafe but is the fastest solution
float xps = FinalPos().x;
if (io->InputHandler->DragObject(
if (io->InputHandler.DragObject(
this->GetID(),
fvec4(FinalPos() + fvec2(2, 0), fvec2(width, GetSize().y)))) {
if (!io->InputHandler->DragReleasedAW) {
if (!io->InputHandler.DragReleasedAW) {
*data = std::clamp(
T(max * (std::clamp(io->InputHandler->DragLastPosition.x - xps, 0.f,
width) /
width)),
T(max *
(std::clamp(io->InputHandler.DragLastPosition.x - xps, 0.f, width) /
width)),
this->min, this->max);
}
}
@@ -69,27 +70,28 @@ template <typename T>
PD_API void Slider<T>::Draw() {
// Assert(io.get() && list.get(), "Did you run Container::Init correctly?");
// io->Ren->OnScreen(screen);
list->SetFont(GetFont());
std::string p;
if constexpr (std::is_floating_point_v<T>) {
p = std::format("{:.{}f}", *data, precision);
} else {
p = std::format("{}", *data);
}
fvec2 td = io->Font->GetTextBounds(p, io->FontScale);
fvec2 td = io->Font->GetTextBounds(p.c_str(), io->FontScale);
list->PathRect(FinalPos(), FinalPos() + fvec2(width, td.y) + io->FramePadding,
io->FrameRounding);
list->PathFill(io->Theme->Get(UI7Color_Button));
list->PathFill(io->Theme.Get(UI7Color_Button));
list->PathRect(FinalPos() + 2 + PD::fvec2(slp, 0),
FinalPos() + fvec2(slp + slw - 2, td.y - 2) + io->FramePadding,
io->FrameRounding);
list->PathFill(io->Theme->Get(UI7Color_ButtonActive));
list->PathFill(io->Theme.Get(UI7Color_ButtonActive));
list->LayerUp();
list->DrawTextEx(FinalPos(), p, io->Theme->Get(UI7Color_Text),
list->DrawTextEx(FinalPos(), p.c_str(), io->Theme.Get(UI7Color_Text),
LiTextFlags_AlignMid, fvec2(width, td.y) + io->FramePadding);
list->LayerDown();
list->DrawText(FinalPos() + fvec2(width + io->FramePadding.x * 2.f,
io->FramePadding.y * 0.5),
label, io->Theme->Get(UI7Color_Text));
label.c_str(), io->Theme.Get(UI7Color_Text));
}
template <typename T>
@@ -98,7 +100,7 @@ PD_API void Slider<T>::Update() {
// Probably need to find a faster solution (caching sizes calculated here)
slw = std::clamp(static_cast<float>(width / max), 3.f, width);
slp = static_cast<float>((float)*data / (float)max) * (width - slw);
fvec2 tdim = io->Font->GetTextBounds(label, io->FontScale);
fvec2 tdim = io->Font->GetTextBounds(label.c_str(), io->FontScale);
this->SetSize(
fvec2(width + tdim.x + io->ItemSpace.x * 2, tdim.y + io->FramePadding.y));
Executable → Regular
+60 -43
View File
@@ -1,44 +1,61 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/core.hpp>
#include <pd/ui7/io.hpp>
namespace PD {
PD_API void UI7::IO::Update() {
/** Todo: find out if we even still use the Drawlist regestry */
u64 current = pCtx.Os()->GetNanoTime();
Delta = static_cast<float>(current - LastTime) / 1000000.f;
LastTime = current;
DeltaStats->Add(Delta * 1000);
Time->Update();
InputHandler->Update();
Framerate = 1000.f / Delta;
DrawListRegestry.clear();
DrawListRegestry.push_front(std::make_pair("CtxBackList", Back));
if (Font) ItemRowHeight = FontScale * Font->PixelHeight;
// RegisterDrawList("CtxBackList", Back);
NumIndices = FDL->pNumIndices;
NumVertices = FDL->pNumVertices;
}
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/core/core.hpp>
#include <pd/drivers/drivers.hpp>
#include <pd/ui7/containers.hpp>
#include <pd/ui7/io.hpp>
namespace PD {
PD_API UI7::IO::IO() : DeltaStats(60), CurrentViewPort("", 0) {
/** Probably not the best solution i guess */
// CurrentViewPort =
// ViewPort::New("Default", ivec4(ivec2(0, 0), pCtx.Gfx()->ViewPort));
// Start a little larger on these
LabelPool.Init(512);
DynObjPool.Init(512);
}
PD_API UI7::IO::~IO() {}
PD_API void UI7::IO::Update() {
/** Todo: find out if we even still use the Drawlist regestry */
u64 current = PD::Os::GetTimeNano();
Delta = static_cast<float>(current - LastTime) / 1000000.f;
LastTime = current;
DeltaStats.Add(Delta * 1000);
Time.Update();
InputHandler.Update();
Framerate = 1000.f / Delta;
DrawlistRegestry.clear();
DrawlistRegestry.push_front(std::make_pair("CtxBackList", &Back));
if (Font) ItemRowHeight = FontScale * Font->PixelHeight;
// RegisterDrawList("CtxBackList", Back);
NumIndices = FDL.GetNumIndices();
NumVertices = FDL.GetNumVertices();
LabelPool.ResetFast();
DynObjPool.ResetFast();
ImagePool.ResetFast();
}
} // namespace PD
+239 -189
View File
@@ -1,190 +1,240 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/containers.hpp>
#include <pd/ui7/layout.hpp>
namespace PD {
namespace UI7 {
PD_API void Layout::CursorInit() { Cursor = fvec2(WorkRect.x, WorkRect.y); }
PD_API void Layout::SameLine() {
BackupCursor = LastObjSize;
Cursor = SamelineCursor;
}
PD_API void Layout::CursorMove(const fvec2& size) {
LastObjSize = size;
SamelineCursor = Cursor + fvec2(size.x + IO->ItemSpace.x, 0);
if (BeforeSameLine.y) {
Cursor =
fvec2(IO->MenuPadding.x, Cursor.y + BeforeSameLine.y + IO->ItemSpace.y);
BeforeSameLine = 0.f;
} else {
Cursor = fvec2(IO->MenuPadding.x + InitialCursorOffset.x,
Cursor.y + size.y + IO->ItemSpace.y);
}
// Logical Issue here as x should use a max check
MaxPosition = fvec2(std::max(MaxPosition.x, SamelineCursor.x), Cursor.y);
}
PD_API bool Layout::ObjectWorkPos(fvec2& movpos) {
if (Scrolling[1]) {
movpos.y -= ScrollOffset.y;
if (!Li::Renderer::InBox(
movpos, LastObjSize,
fvec4(WorkRect.x, WorkRect.y, WorkRect.x + WorkRect.z,
WorkRect.y + WorkRect.w))) {
return true;
}
}
return false;
}
PD_API void Layout::AddObject(Container::Ref obj) {
obj->Init(IO, DrawList);
obj->SetPos(AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
obj->Update();
CursorMove(obj->GetSize());
obj->HandleScrolling(ScrollOffset, WorkRect);
Objects.push_back(obj);
}
PD_API void Layout::AddObjectEx(Container::Ref obj, u32 flags) {
obj->Init(IO, DrawList);
if (!(flags & UI7LytAdd_NoCursorUpdate)) {
obj->SetPos(
AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
}
obj->Update();
if (!(flags & UI7LytAdd_NoCursorUpdate)) {
CursorMove(obj->GetSize());
}
if (!(flags & UI7LytAdd_NoScrollHandle)) {
obj->HandleScrolling(ScrollOffset, WorkRect);
}
if (flags & UI7LytAdd_Front) {
Objects.push_front(obj);
} else {
Objects.push_back(obj);
}
}
PD_API Container::Ref Layout::FindObject(u32 id) {
for (auto& it : IDObjects) {
if (it->GetID() == id) {
return it;
}
}
return nullptr;
}
PD_API fvec2 Layout::AlignPosition(fvec2 pos, fvec2 size, fvec4 area,
UI7Align alignment) {
vec2 p = pos;
if (alignment & UI7Align_Center) {
p.x = (area.x + area.z) * 0.5 - (pos.x - area.x + size.x * 0.5);
} else if (alignment & UI7Align_Right) {
}
if (alignment & UI7Align_Mid) {
p.y = (area.y + area.w) * 0.5 - (pos.y - area.y + size.y * 0.5);
} else if (alignment & UI7Align_Bottom) {
}
return p;
}
PD_API void Layout::Update() {
if (Size == fvec2(0.f)) {
Size = fvec2(MaxPosition) + IO->MenuPadding * 2;
}
for (auto& it : Objects) {
if (it->GetID() != 0 && !FindObject(it->GetID())) {
IDObjects.push_back(it);
}
if (!it->Skippable()) {
it->SetPos(it->GetPos() + Pos);
it->HandleInput();
it->UnlockInput();
if (Flags & UI7LayoutFlags_UseClipRect) {
it->SetClipRect(fvec4(Pos, Size));
}
it->PreDraw();
it->Draw();
it->PostDraw();
}
}
for (auto it = IDObjects.begin(); it != IDObjects.end();) {
if ((*it)->Removable()) {
it = IDObjects.erase(it);
} else {
it++;
}
}
Objects.clear();
WorkRect = fvec4(fvec2(WorkRect.x, WorkRect.y), Size - IO->MenuPadding);
CursorInit();
}
/** SECTION CONTAINERS (STOLEN FROM FORMER MENU) */
PD_API void Layout::Label(const std::string& label) {
// Layout API
auto r = Label::New(label, IO);
r->SetClipRect(fvec4(GetPosition(), GetPosition() + GetSize()));
AddObject(r);
}
PD_API bool Layout::Button(const std::string& label) {
bool ret = false;
u32 id = Strings::FastHash("btn" + label + std::to_string(Objects.size()));
Container::Ref r = FindObject(id);
if (!r) {
r = Button::New(label, IO);
r->SetID(id);
}
AddObject(r);
if (!r->Skippable()) {
ret = std::static_pointer_cast<UI7::Button>(r)->IsPressed();
}
return ret;
}
PD_API void Layout::Checkbox(const std::string& label, bool& v) {
u32 id = Strings::FastHash("cbx" + label + std::to_string(Objects.size()));
Container::Ref r = FindObject(id);
if (!r) {
r = Checkbox::New(label, v, IO);
r->SetID(id);
}
AddObject(r);
}
PD_API void Layout::Image(Li::Texture::Ref img, fvec2 size, Li::Rect uv) {
Container::Ref r = Image::New(img, size, uv);
AddObject(r);
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <algorithm>
#include <pd/ui7/containers.hpp>
#include <pd/ui7/layout.hpp>
namespace PD {
namespace UI7 {
PD_API Layout::~Layout() {
// We all love managing memory i guess
for (Container* obj : IDObjects) {
delete obj;
}
IDObjects.clear();
}
PD_API void Layout::CursorInit() { Cursor = fvec2(WorkRect.x, WorkRect.y); }
PD_API void Layout::SameLine() {
BackupCursor = LastObjSize;
Cursor = SamelineCursor;
}
PD_API void Layout::CursorMove(const fvec2& size) {
LastObjSize = size;
SamelineCursor = Cursor + fvec2(size.x + IO.ItemSpace.x, 0);
if (BeforeSameLine.y) {
Cursor =
fvec2(IO.MenuPadding.x, Cursor.y + BeforeSameLine.y + IO.ItemSpace.y);
BeforeSameLine = 0.f;
} else {
Cursor = fvec2(IO.MenuPadding.x + InitialCursorOffset.x,
Cursor.y + size.y + IO.ItemSpace.y);
}
// Logical Issue here as x should use a max check
MaxPosition = fvec2(std::max(MaxPosition.x, SamelineCursor.x), Cursor.y);
}
PD_API bool Layout::ObjectWorkPos(fvec2& movpos) {
if (Scrolling[1]) {
movpos.y -= ScrollOffset.y;
if (!Li::Math::InBounds(
movpos, LastObjSize,
fvec4(WorkRect.x, WorkRect.y, WorkRect.x + WorkRect.z,
WorkRect.y + WorkRect.w))) {
return true;
}
}
return false;
}
PD_API void Layout::AddObject(Container* obj) {
obj->Init(&IO, &DrawList);
obj->SetPos(AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
obj->Update();
CursorMove(obj->GetSize());
obj->HandleScrolling(ScrollOffset, WorkRect);
Objects.push_back(obj);
}
PD_API void Layout::AddObjectEx(Container* obj, u32 flags) {
obj->Init(&IO, &DrawList);
if (!(flags & UI7LytAdd_NoCursorUpdate)) {
obj->SetPos(
AlignPosition(Cursor, obj->GetSize(), WorkRect, GetAlignment()));
}
obj->Update();
if (!(flags & UI7LytAdd_NoCursorUpdate)) {
CursorMove(obj->GetSize());
}
if (!(flags & UI7LytAdd_NoScrollHandle)) {
obj->HandleScrolling(ScrollOffset, WorkRect);
}
if (flags & UI7LytAdd_Front) {
Objects.push_front(obj);
} else {
Objects.push_back(obj);
}
}
PD_API Container* Layout::FindObject(u32 id) {
for (auto& it : IDObjects) {
if (it->GetID() == id) {
return it;
}
}
return nullptr;
}
PD_API fvec2 Layout::AlignPosition(fvec2 pos, fvec2 size, fvec4 area,
UI7Align alignment) {
vec2 p = pos;
if (alignment & UI7Align_Center) {
p.x = (area.x + area.z) * 0.5 - (pos.x - area.x + size.x * 0.5);
} else if (alignment & UI7Align_Right) {
}
if (alignment & UI7Align_Mid) {
p.y = (area.y + area.w) * 0.5 - (pos.y - area.y + size.y * 0.5);
} else if (alignment & UI7Align_Bottom) {
}
return p;
}
PD_API void Layout::HandleScrolling() {
if (Flags & UI7LayoutFlags_VtScrolling) {
bool allowed = MaxPosition.y > WorkRect.w;
if (allowed) {
if (PD::Hid::IsEvent(Hid::Event::Down, PD::Hid::Gamepad::Touch) ||
PD::Hid::IsEvent(Hid::Event::Down, PD::Hid::Keyboard::MouseLeft)) {
ScrollStart = ScrollOffset;
}
if (IO.InputHandler.DragObject(UI7::ID("sbg" + ID.GetName()),
fvec4(Pos, fvec2(0.f)) + WorkRect)) {
if (!IO.InputHandler.DragReleasedAW) {
ScrollOffset.y =
std::clamp(ScrollStart.y + IO.InputHandler.DragSourcePos.y -
IO.InputHandler.DragPosition.y,
-20.f, MaxPosition.y - WorkRect.w + 20.f);
}
}
} else {
ScrollOffset.y = 0.f;
}
if (ScrollOffset.y > MaxPosition.y - WorkRect.w) {
ScrollOffset.y -= 1.5f;
if (ScrollOffset.y < MaxPosition.y - WorkRect.w) {
ScrollOffset.y = MaxPosition.y - WorkRect.w;
}
}
if (ScrollOffset.y < 0) {
ScrollOffset.y += 1.5f;
if (ScrollOffset.y > 0) {
ScrollOffset.y = 0;
}
}
}
}
PD_API void Layout::Update() {
if (Size == fvec2(0.f)) {
Size = fvec2(MaxPosition) + IO.MenuPadding * 2;
}
for (auto& it : Objects) {
if (it->GetID() != 0 && !FindObject(it->GetID())) {
IDObjects.push_back(it);
}
if (!it->Skippable()) {
it->SetPos(it->GetPos() + Pos);
it->HandleInput();
it->UnlockInput();
if (Flags & UI7LayoutFlags_UseClipRect) {
it->SetClipRect(fvec4(Pos, Size));
}
it->PreDraw();
it->Draw();
it->PostDraw();
}
}
for (auto it = IDObjects.begin(); it != IDObjects.end();) {
if ((*it)->Removable()) {
delete *it;
it = IDObjects.erase(it);
} else {
it++;
}
}
Objects.clear();
WorkRect = fvec4(fvec2(WorkRect.x, WorkRect.y), Size - IO.MenuPadding);
CursorInit();
HandleScrolling();
}
/** SECTION CONTAINERS (STOLEN FROM FORMER MENU) */
PD_API void Layout::Label(const std::string& label) {
// Layout API
auto r = IO.LabelPool.Allocate();
*r = UI7::Label(label, IO);
r->SetClipRect(fvec4(GetPosition(), GetPosition() + GetSize()));
AddObject(r);
}
PD_API bool Layout::Button(const std::string& label) {
bool ret = false;
u32 id = Strings::FastHash("btn" + label + std::to_string(Objects.size()));
Container* r = FindObject(id);
if (!r) {
r = new UI7::Button(label, IO);
r->SetID(id);
}
AddObject(r);
if (!r->Skippable()) {
ret = reinterpret_cast<UI7::Button*>(r)->IsPressed();
}
return ret;
}
PD_API void Layout::Checkbox(const std::string& label, bool& v) {
u32 id = Strings::FastHash("cbx" + label + std::to_string(Objects.size()));
Container* r = FindObject(id);
if (!r) {
r = new UI7::Checkbox(label, v, IO);
r->SetID(id);
}
AddObject(r);
}
PD_API void Layout::Image(Li::Texture img, fvec2 size, Li::Rect uv) {
auto r = IO.ImagePool.Allocate();
*r = UI7::Image(img, size, uv);
AddObject(r);
}
} // namespace UI7
} // namespace PD
+177 -206
View File
@@ -21,175 +21,141 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <algorithm>
#include <pd/ui7/containers.hpp>
#include <pd/ui7/menu.hpp>
namespace PD {
namespace UI7 {
Menu::Menu(const ID& id, IO::Ref io) : pIO(io), pID(id) {
pLayout = Layout::New(id, io);
TitleBarHeight = pIO->FontScale * pIO->Font->PixelHeight + pIO->MenuPadding.y;
pLayout->WorkRect.y += TitleBarHeight;
pLayout->Flags |= UI7LayoutFlags_UseClipRect;
pLayout->CursorInit();
Menu::Menu(const ID& id, IO& io) : pIO(io), pID(id), pLayout(id, io) {
TitleBarHeight = pIO.FontScale * pIO.Font->PixelHeight + pIO.MenuPadding.y;
pLayout.WorkRect.y += TitleBarHeight;
pLayout.Flags |= UI7LayoutFlags_UseClipRect;
pLayout.CursorInit();
}
PD_API void Menu::Label(const std::string& label) {
// Layout API
auto r = Label::New(label, pIO);
pLayout->AddObject(r);
auto r = pIO.LabelPool.Allocate();
*r = UI7::Label(label, pIO);
pLayout.AddObject(r);
}
PD_API bool Menu::Button(const std::string& label) {
bool ret = false;
u32 id = Strings::FastHash("btn" + label +
std::to_string(pLayout->Objects.size()));
Container::Ref r = pLayout->FindObject(id);
u32 id =
Strings::FastHash("btn" + label + std::to_string(pLayout.Objects.size()));
Container* r = pLayout.FindObject(id);
if (!r) {
r = Button::New(label, pIO);
r = new UI7::Button(label, pIO);
r->SetID(id);
}
pLayout->AddObject(r);
pLayout.AddObject(r);
if (!r->Skippable()) {
ret = std::static_pointer_cast<UI7::Button>(r)->IsPressed();
ret = reinterpret_cast<UI7::Button*>(r)->IsPressed();
}
return ret;
}
PD_API void Menu::Checkbox(const std::string& label, bool& v) {
u32 id = Strings::FastHash("cbx" + label +
std::to_string(pLayout->Objects.size()));
Container::Ref r = pLayout->FindObject(id);
u32 id =
Strings::FastHash("cbx" + label + std::to_string(pLayout.Objects.size()));
Container* r = pLayout.FindObject(id);
if (!r) {
r = Checkbox::New(label, v, pIO);
r = new UI7::Checkbox(label, v, pIO);
r->SetID(id);
}
pLayout->AddObject(r);
pLayout.AddObject(r);
}
PD_API void Menu::Image(Li::Texture::Ref img, fvec2 size, Li::Rect uv) {
Container::Ref r = Image::New(img, size, uv);
pLayout->AddObject(r);
PD_API void Menu::Image(Li::Texture img, fvec2 size, Li::Rect uv) {
auto r = pIO.ImagePool.Allocate();
*r = UI7::Image(img, size, uv);
pLayout.AddObject(r);
}
PD_API void Menu::ColorEdit(const std::string& label, u32& clr) {
u32 id = Strings::FastHash("drd" + label);
Container::Ref r = pLayout->FindObject(id);
Container* r = pLayout.FindObject(id);
if (!r) {
r = UI7::ColorEdit::New(label, &clr, pIO);
r = new UI7::ColorEdit(label, &clr, pIO);
r->SetID(id);
}
pLayout->AddObject(r);
pLayout.AddObject(r);
}
PD_API void Menu::Separator() {
// Dynamic Objects are very simple...
Container::Ref r = DynObj::New(
[=, this](UI7::IO::Ref io, Li::DrawList::Ref l, UI7::Container* self) {
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj(
[=, this](UI7::IO* io, Li::Drawlist* l, UI7::Container* self) {
l->DrawRectFilled(self->FinalPos(), self->GetSize(),
pIO->Theme->Get(UI7Color_TextDead));
pIO.Theme.Get(UI7Color_TextDead));
});
// Set size before pushing (cause Cursor Move will require it)
r->SetSize(fvec2(
pLayout->Size.x - pIO->MenuPadding.x * 2 - pLayout->InitialCursorOffset.x,
pLayout.Size.x - pIO.MenuPadding.x * 2 - pLayout.InitialCursorOffset.x,
1));
pLayout->AddObject(r);
pLayout.AddObject(r);
}
PD_API void Menu::SeparatorText(const std::string& label) {
// Also note to use [=] instead of [&] to not undefined access label
Container::Ref r = DynObj::New([=, this](UI7::IO::Ref io, Li::DrawList::Ref l,
UI7::Container* self) {
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj([=, this](UI7::IO* io, Li::Drawlist* l,
UI7::Container* self) {
l->SetFont(self->GetFont());
fvec2 size = self->GetSize();
fvec2 tdim = io->Font->GetTextBounds(label, io->FontScale);
fvec2 tdim = io->Font->GetTextBounds(label.c_str(), io->FontScale);
fvec2 pos = self->FinalPos();
auto align = pLayout->GetAlignment();
vec2 rpos = pLayout->AlignPosition(
pos, tdim, fvec4(pLayout->Pos, pLayout->Size), align);
auto align = pLayout.GetAlignment();
vec2 rpos = pLayout.AlignPosition(pos, tdim,
fvec4(pLayout.Pos, pLayout.Size), align);
if (!(align & UI7Align_Left)) {
l->DrawRectFilled(fvec2(rpos.x + io->FramePadding.x, tdim.y * 0.5),
fvec2(pos.x - rpos.x - io->MenuPadding.x, 1),
io->Theme->Get(UI7Color_TextDead));
io->Theme.Get(UI7Color_TextDead));
}
if (!(align & UI7Align_Right)) {
l->DrawRectFilled(pos + fvec2(tdim.x + io->FramePadding.x, tdim.y * 0.5),
fvec2(size.x - tdim.x - io->MenuPadding.x, 1),
io->Theme->Get(UI7Color_TextDead));
io->Theme.Get(UI7Color_TextDead));
}
l->DrawTextEx(rpos, label, io->Theme->Get(UI7Color_Text), 0,
fvec2(pLayout->Size.x, self->GetSize().y));
l->DrawTextEx(rpos, label.c_str(), io->Theme.Get(UI7Color_Text), 0,
fvec2(pLayout.Size.x, self->GetSize().y));
});
// Set size before pushing (cause Cursor Move will require it)
r->SetSize(fvec2(
pLayout->Size.x - pIO->MenuPadding.x * 2 - pLayout->InitialCursorOffset.x,
pIO->Font->PixelHeight * pIO->FontScale));
pLayout->AddObject(r);
pLayout.Size.x - pIO.MenuPadding.x * 2 - pLayout.InitialCursorOffset.x,
pIO.Font->PixelHeight * pIO.FontScale));
pLayout.AddObject(r);
}
PD_API void Menu::HandleFocus() {
// Check if menu can be focused for Selective Menu Input API
vec4 newarea = fvec4(pLayout->Pos, pLayout->Size);
vec4 newarea = fvec4(pLayout.Pos, pLayout.Size);
if (!pIsOpen) {
newarea = fvec4(pLayout->Pos, fvec2(pLayout->Size.x, TitleBarHeight));
newarea = fvec4(pLayout.Pos, fvec2(pLayout.Size.x, TitleBarHeight));
}
if ((pIO->pCtx.Hid()->IsDown(Hid::Key::Touch) ||
pIO->pCtx.Hid()->IsEvent(Hid::Event::Event_Down, HidKb::Kb_MouseLeft)) &&
Li::Renderer::InBox(pIO->pCtx.Hid()->MousePos(), newarea) &&
!Li::Renderer::InBox(pIO->pCtx.Hid()->MousePos(),
pIO->InputHandler->FocusedMenuRect)) {
pIO->InputHandler->FocusedMenu = pID;
if ((PD::Hid::IsEvent(Hid::Event::Down, Hid::Gamepad::Touch) ||
PD::Hid::IsEvent(Hid::Event::Down, Hid::Keyboard::MouseLeft)) &&
Li::Math::InBounds(PD::Hid::MousePos(), newarea) &&
!Li::Math::InBounds(PD::Hid::MousePos(),
pIO.InputHandler.FocusedMenuRect)) {
pIO.InputHandler.FocusedMenu = pID;
}
if (pIO->InputHandler->FocusedMenu == pID) {
pIO->InputHandler->FocusedMenuRect = newarea;
}
}
/** Todo: (func name is self describing) */
PD_API void Menu::HandleScrolling() {
if (Flags & UI7MenuFlags_VtScrolling) {
bool allowed =
pLayout->MaxPosition.y > (pLayout->WorkRect.w - pLayout->WorkRect.y);
if (allowed) {
if (pIO->pCtx.Hid()->IsDown(PD::Hid::Key::Touch)) {
pLayout->ScrollStart = pLayout->ScrollOffset;
}
if (pIO->InputHandler->DragObject(
"sbg" + pID.GetName(),
fvec4(pLayout->Pos, fvec2(0.f)) + pLayout->WorkRect)) {
if (pIO->InputHandler->DragReleasedAW) {
} else {
pLayout->ScrollOffset.y = std::clamp(
pLayout->ScrollStart.y + pIO->InputHandler->DragSourcePos.y -
pIO->InputHandler->DragPosition.y,
-20.f, pLayout->MaxPosition.y - 220);
}
}
} else {
pLayout->ScrollOffset.y = 0.f;
}
if (pLayout->ScrollOffset.y > pLayout->MaxPosition.y - 240) {
pLayout->ScrollOffset.y -= 1.5;
if (pLayout->ScrollOffset.y < pLayout->MaxPosition.y - 240) {
pLayout->ScrollOffset.y = pLayout->MaxPosition.y - 240;
}
}
if (pLayout->ScrollOffset.y < 0) {
pLayout->ScrollOffset.y += 1.5;
if (pLayout->ScrollOffset.y > 0) {
pLayout->ScrollOffset.y = 0;
}
}
if (pIO.InputHandler.FocusedMenu == pID) {
pIO.InputHandler.FocusedMenuRect = newarea;
}
}
PD_API void Menu::HandleTitlebarActions() {
// Collapse
if (!(Flags & UI7MenuFlags_NoCollapse)) {
vec2 cpos = pLayout->Pos + pIO->FramePadding;
vec2 cpos = pLayout.Pos + pIO.FramePadding;
// clr_collapse_tri = UI7Color_FrameBackground;
if (pIO->InputHandler->DragObject(UI7::ID(pID.GetName() + "clbse"),
fvec4(cpos, fvec2(18, TitleBarHeight)))) {
if (pIO->InputHandler->DragReleased) {
if (pIO.InputHandler.DragObject(UI7::ID(pID.GetName() + "clbse"),
fvec4(cpos, fvec2(18, TitleBarHeight)))) {
if (pIO.InputHandler.DragReleased) {
pIsOpen = !pIsOpen;
}
// clr_collapse_tri = UI7Color_FrameBackgroundHovered;
@@ -197,15 +163,15 @@ PD_API void Menu::HandleTitlebarActions() {
}
// Close Logic
if (!(Flags & UI7MenuFlags_NoClose) && pIsShown != nullptr) {
fvec2 size = TitleBarHeight - pIO->FramePadding.y * 2;
fvec2 size = TitleBarHeight - pIO.FramePadding.y * 2;
fvec2 cpos =
fvec2(pLayout->Pos.x + pLayout->Size.x - size.x - pIO->FramePadding.x,
pLayout->Pos.y + pIO->FramePadding.y);
fvec2(pLayout.Pos.x + pLayout.Size.x - size.x - pIO.FramePadding.x,
pLayout.Pos.y + pIO.FramePadding.y);
// clr_close_btn = UI7Color_FrameBackground;
if (pIO->InputHandler->DragObject(UI7::ID(pID.GetName() + "clse"),
fvec4(cpos, size))) {
if (pIO->InputHandler->DragReleased) {
if (pIO.InputHandler.DragObject(UI7::ID(pID.GetName() + "clse"),
fvec4(cpos, size))) {
if (pIO.InputHandler.DragReleased) {
*pIsShown = !(*pIsShown);
}
// clr_close_btn = UI7Color_FrameBackgroundHovered;
@@ -213,36 +179,36 @@ PD_API void Menu::HandleTitlebarActions() {
}
// Resize logic
if (!(Flags & UI7MenuFlags_NoResize)) {
vec2 cpos = pLayout->Pos + pLayout->Size - fvec2(20);
vec2 cpos = pLayout.Pos + pLayout.Size - fvec2(20);
// clr_close_btn = UI7Color_FrameBackground;
if (pIO->InputHandler->DragObject(UI7::ID(pID.GetName() + "rszs"),
fvec4(cpos, fvec2(20)))) {
fvec2 szs = pLayout->Size + (pIO->InputHandler->DragPosition -
pIO->InputHandler->DragLastPosition);
if (pIO.InputHandler.DragObject(UI7::ID(pID.GetName() + "rszs"),
fvec4(cpos, fvec2(20)))) {
fvec2 szs = pLayout.Size + (pIO.InputHandler.DragPosition -
pIO.InputHandler.DragLastPosition);
if (szs.x < 30) szs.x = 30;
if (szs.y < 30) szs.y = 30;
pLayout->Size = szs;
pLayout.Size = szs;
// clr_close_btn = UI7Color_FrameBackgroundHovered;
}
}
// Menu Movement
if (!(Flags & UI7MenuFlags_NoMove)) {
if (pIO->InputHandler->DragObject(
if (pIO.InputHandler.DragObject(
pID.GetName() + "tmv",
fvec4(pLayout->Pos, fvec2(pLayout->Size.x, TitleBarHeight)))) {
if (pIO->InputHandler->DragDoubleRelease) {
fvec4(pLayout.Pos, fvec2(pLayout.Size.x, TitleBarHeight)))) {
if (pIO.InputHandler.DragDoubleRelease) {
pIsOpen = !pIsOpen;
}
pLayout->Pos = pLayout->Pos + (pIO->InputHandler->DragPosition -
pIO->InputHandler->DragLastPosition);
pLayout.Pos = pLayout.Pos + (pIO.InputHandler.DragPosition -
pIO.InputHandler.DragLastPosition);
// Keep Window In Viewport
// Maybe i need to add some operators to vec
pLayout->Pos.x = std::clamp<float>(pLayout->Pos.x, -pLayout->Size.x + 10,
pIO->CurrentViewPort->pSize.z - 10);
pLayout->Pos.y =
std::clamp<float>(pLayout->Pos.y, pIO->CurrentViewPort->pSize.y,
pIO->CurrentViewPort->pSize.w - 10);
pLayout.Pos.x = std::clamp<float>(pLayout.Pos.x, -pLayout.Size.x + 10,
pIO.CurrentViewPort.pSize.z - 10);
pLayout.Pos.y =
std::clamp<float>(pLayout.Pos.y, pIO.CurrentViewPort.pSize.y,
pIO.CurrentViewPort.pSize.w - 10);
}
}
}
@@ -251,67 +217,71 @@ PD_API void Menu::DrawBaseLayout() {
if (pIsOpen) {
/** Resize Sym (Render on Top of Everything) */
if (!(Flags & UI7MenuFlags_NoResize)) {
Container::Ref r = DynObj::New(
[](IO::Ref io, Li::DrawList::Ref l, UI7::Container* self) {
l->Layer(1);
l->PathAdd(self->FinalPos() + self->GetSize() - fvec2(0, 20));
l->PathAdd(self->FinalPos() + self->GetSize());
l->PathAdd(self->FinalPos() + self->GetSize() - fvec2(20, 0));
l->PathFill(io->Theme->Get(UI7Color_Button));
});
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj([](IO* io, Li::Drawlist* l, UI7::Container* self) {
l->SetLayer(1);
l->PathAdd(self->FinalPos() + self->GetSize() - fvec2(0, 20));
l->PathAdd(self->FinalPos() + self->GetSize());
l->PathAdd(self->FinalPos() + self->GetSize() - fvec2(20, 0));
l->PathFill(io->Theme.Get(UI7Color_Button));
l->SetLayer(0);
});
r->SetSize(
fvec2(pLayout->GetSize().x, pLayout->GetSize().y - TitleBarHeight));
fvec2(pLayout.GetSize().x, pLayout.GetSize().y - TitleBarHeight));
r->SetPos(fvec2(0, TitleBarHeight));
pLayout->AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
pLayout.AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
}
/** Background */
Container::Ref r = DynObj::New([](IO::Ref io, Li::DrawList::Ref l,
UI7::Container* self) {
l->Layer(0);
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj([](IO* io, Li::Drawlist* l, UI7::Container* self) {
l->SetLayer(0);
l->PathRectEx(self->FinalPos(), self->FinalPos() + self->GetSize(), 10.f,
LiPathRectFlags_KeepTop | LiPathRectFlags_KeepBot);
l->PathFill(io->Theme->Get(UI7Color_Background));
l->PathFill(io->Theme.Get(UI7Color_Background));
/*l->DrawRectFilled(self->FinalPos(), self->GetSize(),
io->Theme->Get(UI7Color_Background));*/
io->Theme.Get(UI7Color_Background));*/
});
// Set size before pushing (cause Cursor Move will require it)
r->SetSize(
fvec2(pLayout->GetSize().x, pLayout->GetSize().y - TitleBarHeight));
fvec2(pLayout.GetSize().x, pLayout.GetSize().y - TitleBarHeight));
r->SetPos(fvec2(0, TitleBarHeight));
pLayout->AddObjectEx(r, UI7LytAdd_NoCursorUpdate |
UI7LytAdd_NoScrollHandle | UI7LytAdd_Front);
pLayout.AddObjectEx(r, UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle |
UI7LytAdd_Front);
}
if (!(Flags & UI7MenuFlags_NoTitlebar)) {
Container::Ref r = DynObj::New(
[=, this](UI7::IO::Ref io, Li::DrawList::Ref l, UI7::Container* self) {
l->Layer(20);
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj(
[=, this](UI7::IO* io, Li::Drawlist* l, UI7::Container* self) {
l->SetFont(self->GetFont());
l->SetLayer(20);
/** Header Bar */
l->DrawRectFilled(self->FinalPos(), self->GetSize(),
io->Theme->Get(UI7Color_Header));
l->Layer(21);
io->Theme.Get(UI7Color_Header));
l->SetLayer(21);
/** Inline if statement to shift the Text if collapse sym is shown */
/** What the hell is this code btw (didn't found a better way) */
l->DrawText(self->FinalPos() + fvec2((Flags & UI7MenuFlags_NoCollapse)
? pIO->FramePadding.x
: (TitleBarHeight -
pIO->FramePadding.y * 2 +
(io->FramePadding.x * 2)),
2),
pID.GetName(), io->Theme->Get(UI7Color_Text));
l->DrawText(self->FinalPos() +
fvec2((Flags & UI7MenuFlags_NoCollapse)
? pIO.FramePadding.x
: (TitleBarHeight - pIO.FramePadding.y * 2 +
(io->FramePadding.x * 2)),
2),
pID.GetName().c_str(), io->Theme.Get(UI7Color_Text));
l->SetLayer(0);
});
r->SetSize(fvec2(pLayout->GetSize().x, TitleBarHeight));
r->SetSize(fvec2(pLayout.GetSize().x, TitleBarHeight));
r->SetPos(0);
pLayout->AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
pLayout.AddObjectEx(r, UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
/** Collapse Sym */
if (!(Flags & UI7MenuFlags_NoCollapse)) {
r = DynObj::New([=, this](UI7::IO::Ref io, Li::DrawList::Ref l,
UI7::Container* self) {
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj([=, this](UI7::IO* io, Li::Drawlist* l,
UI7::Container* self) {
/** This sym actually requires layer 21 (i dont know why) */
l->Layer(21);
l->SetLayer(21);
/**
* Symbol (Position Swapping set by pIsOpen ? openpos : closepos;)
*/
@@ -321,52 +291,53 @@ PD_API void Menu::DrawBaseLayout() {
fvec2(self->GetSize().x, pIsOpen ? 0 : self->GetSize().y * 0.5),
self->FinalPos() +
fvec2(pIsOpen ? self->GetSize().x * 0.5 : 0, self->GetSize().y),
io->Theme->Get(UI7Color_FrameBackground));
io->Theme.Get(UI7Color_FrameBackground));
l->SetLayer(0);
});
r->SetSize(TitleBarHeight - pIO->FramePadding.y * 2);
r->SetPos(pIO->FramePadding);
pLayout->AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
r->SetSize(TitleBarHeight - pIO.FramePadding.y * 2);
r->SetPos(pIO.FramePadding);
pLayout.AddObjectEx(r,
UI7LytAdd_NoCursorUpdate | UI7LytAdd_NoScrollHandle);
}
/** Close Sym (only shown if pIsShown is not nullptr) */
if (!(Flags & UI7MenuFlags_NoClose) && pIsShown) {
fvec2 size = TitleBarHeight - pIO->FramePadding.y * 2; // Fixed quad size
fvec2 size = TitleBarHeight - pIO.FramePadding.y * 2; // Fixed quad size
// Need to clamp this way as the math lib lacks a less and greater
// operator in vec2 (don't checked if it would make sense yet)
size.x = std::clamp(size.x, 5.f, std::numeric_limits<float>::max());
size.y = std::clamp(size.y, 5.f, std::numeric_limits<float>::max());
// Probably should fix the minsize to be locked on y
fvec2 cpos =
fvec2(pLayout->Pos.x + pLayout->Size.x - size.x - pIO->FramePadding.x,
pLayout->Pos.y + pIO->FramePadding.y);
pLayout->DrawList->DrawLine(cpos, cpos + size,
pIO->Theme->Get(UI7Color_FrameBackground), 2);
pLayout->DrawList->DrawLine(cpos + fvec2(0, size.y),
cpos + fvec2(size.x, 0),
pIO->Theme->Get(UI7Color_FrameBackground), 2);
fvec2(pLayout.Pos.x + pLayout.Size.x - size.x - pIO.FramePadding.x,
pLayout.Pos.y + pIO.FramePadding.y);
pLayout.DrawList.DrawLine(cpos, cpos + size,
pIO.Theme.Get(UI7Color_FrameBackground), 2);
pLayout.DrawList.DrawLine(cpos + fvec2(0, size.y),
cpos + fvec2(size.x, 0),
pIO.Theme.Get(UI7Color_FrameBackground), 2);
}
}
}
PD_API void Menu::Update() {
HandleFocus();
if (pLayout->Size == fvec2(0.f) || Flags & UI7MenuFlags_AlwaysAutoSize) {
pLayout->Size = fvec2(pLayout->MaxPosition) + pIO->MenuPadding * 2;
if (pLayout.Size == fvec2(0.f) || Flags & UI7MenuFlags_AlwaysAutoSize) {
pLayout.Size = fvec2(pLayout.MaxPosition) + pIO.MenuPadding * 2;
}
if (Flags & UI7MenuFlags_VtScrolling)
pLayout.Flags |= UI7LayoutFlags_VtScrolling;
if (Flags & UI7MenuFlags_HzScrolling)
pLayout.Flags |= UI7LayoutFlags_HzScrolling;
if (!(Flags & UI7MenuFlags_NoTitlebar)) {
TitleBarHeight =
pIO->FontScale * pIO->Font->PixelHeight + pIO->MenuPadding.y;
pLayout->WorkRect.y = 5.f + TitleBarHeight;
TitleBarHeight = pIO.FontScale * pIO.Font->PixelHeight + pIO.MenuPadding.y;
pLayout.WorkRect.y = 5.f + TitleBarHeight;
HandleTitlebarActions();
} else {
TitleBarHeight = 0.f;
pLayout->WorkRect.y = 5.f;
pLayout.WorkRect.y = 5.f;
}
DrawBaseLayout();
pLayout->Update();
if (Flags & UI7MenuFlags_VtScrolling || Flags & UI7MenuFlags_HzScrolling) {
HandleScrolling();
}
pLayout.Update();
}
PD_API bool Menu::BeginTreeNode(const ID& id) {
@@ -376,36 +347,36 @@ PD_API bool Menu::BeginTreeNode(const ID& id) {
pTreeNodes[id] = false;
n = pTreeNodes.find(id);
}
fvec2 pos = pLayout->Cursor;
fvec2 tdim = pIO->Font->GetTextBounds(id.GetName(), pIO->FontScale);
fvec2 szs = tdim + fvec2(pIO->ItemSpace.x + 10, 0);
fvec2 pos = pLayout.Cursor;
fvec2 tdim = pIO.Font->GetTextBounds(id.GetName().c_str(), pIO.FontScale);
fvec2 szs = tdim + fvec2(pIO.ItemSpace.x + 10, 0);
if (n->second) {
pLayout->InitialCursorOffset += 10.f;
pLayout.InitialCursorOffset += 10.f;
}
// Object
auto r =
DynObj::New([=, this](IO::Ref io, Li::DrawList::Ref l, Container* self) {
fvec2 ts = self->FinalPos() + fvec2(0, 7);
fvec2 pl[2] = {fvec2(10, 5), fvec2(0, 10)};
if (n->second) {
float t = pl[0].y;
pl[0].y = pl[1].x;
pl[1].x = t;
}
l->DrawTriangleFilled(ts, ts + pl[0], ts + pl[1],
io->Theme->Get(UI7Color_FrameBackground));
DynObj* r = pIO.DynObjPool.Allocate();
*r = UI7::DynObj([=, this](IO* io, Li::Drawlist* l, Container* self) {
l->SetFont(self->GetFont());
fvec2 ts = self->FinalPos() + fvec2(0, 7);
fvec2 pl[2] = {fvec2(10, 5), fvec2(0, 10)};
if (n->second) {
float t = pl[0].y;
pl[0].y = pl[1].x;
pl[1].x = t;
}
l->DrawTriangleFilled(ts, ts + pl[0], ts + pl[1],
io->Theme.Get(UI7Color_FrameBackground));
l->DrawText(self->FinalPos() + fvec2(10 + io->ItemSpace.x, 0),
id.GetName(), io->Theme->Get(UI7Color_Text));
});
l->DrawText(self->FinalPos() + fvec2(10 + io->ItemSpace.x, 0),
id.GetName().c_str(), io->Theme.Get(UI7Color_Text));
});
/** Yes this new function handler was created for tree nodes */
r->AddInputHandler([=, this](IO::Ref io, Container* self) {
if (io->InputHandler->DragObject(
ID(pID.GetName() + id.GetName()),
fvec4(self->FinalPos(), self->GetSize()))) {
if (io->InputHandler->DragReleased) {
r->AddInputHandler([=, this](IO* io, Container* self) {
if (io->InputHandler.DragObject(ID(pID.GetName() + id.GetName()),
fvec4(self->FinalPos(), self->GetSize()))) {
if (io->InputHandler.DragReleased) {
n->second = !n->second;
}
}
@@ -413,16 +384,16 @@ PD_API bool Menu::BeginTreeNode(const ID& id) {
r->SetPos(pos);
r->SetSize(szs);
/** Use Add Object as it is faster */
pLayout->AddObject(r);
pLayout.AddObject(r);
return n->second;
}
PD_API void UI7::Menu::EndTreeNode() {
pLayout->InitialCursorOffset.x -= 10.f;
pLayout->Cursor.x -= 10.f;
if (pLayout->InitialCursorOffset.x < 0.f) {
pLayout->InitialCursorOffset.x = 0.f;
pLayout.InitialCursorOffset.x -= 10.f;
pLayout.Cursor.x -= 10.f;
if (pLayout.InitialCursorOffset.x < 0.f) {
pLayout.InitialCursorOffset.x = 0.f;
}
}
} // namespace UI7
Executable → Regular
+67 -67
View File
@@ -1,68 +1,68 @@
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/theme.hpp>
namespace PD {
namespace UI7 {
PD_API void Theme::Default(Theme& theme) {
theme.Set(UI7Color_Text, Color("#FFFFFFFF"));
theme.Set(UI7Color_TextDead, Color("#AAAAAAFF"));
theme.Set(UI7Color_Background, Color("#222222aa"));
theme.Set(UI7Color_Border, Color("#999999ff"));
theme.Set(UI7Color_Button, Color("#111111FF"));
theme.Set(UI7Color_ButtonDead, Color("#080808FF"));
theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF"));
theme.Set(UI7Color_ButtonHovered, Color("#222222FF"));
theme.Set(UI7Color_Header, Color("#111111cc"));
theme.Set(UI7Color_HeaderDead, Color("#080808FF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF"));
theme.Set(UI7Color_FrameBackground, Color("#555555FF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
PD_API void Theme::Flashbang(Theme& theme) {
theme.Set(UI7Color_Text, Color("#000000FF"));
theme.Set(UI7Color_TextDead, Color("#333333FF"));
theme.Set(UI7Color_Background, Color("#eeeeeeFF"));
theme.Set(UI7Color_Border, Color("#777777ff"));
theme.Set(UI7Color_Button, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonDead, Color("#bbbbbbFF"));
theme.Set(UI7Color_ButtonActive, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonHovered, Color("#acacacFF"));
theme.Set(UI7Color_Header, Color("#ddddddFF"));
theme.Set(UI7Color_HeaderDead, Color("#cdcdcdFF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#ccccccFF"));
theme.Set(UI7Color_FrameBackground, Color("#aaaaaaFF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#909090FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
} // namespace UI7
/*
MIT License
Copyright (c) 2024 - 2026 René Amthor (tobid7)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/ui7/theme.hpp>
namespace PD {
namespace UI7 {
PD_API void Theme::Default(Theme& theme) {
theme.Set(UI7Color_Text, Color("#FFFFFFFF"));
theme.Set(UI7Color_TextDead, Color("#AAAAAAFF"));
theme.Set(UI7Color_Background, Color("#222222aa"));
theme.Set(UI7Color_Border, Color("#999999ff"));
theme.Set(UI7Color_Button, Color("#111111FF"));
theme.Set(UI7Color_ButtonDead, Color("#080808FF"));
theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF"));
theme.Set(UI7Color_ButtonHovered, Color("#222222FF"));
theme.Set(UI7Color_Header, Color("#111111cc"));
theme.Set(UI7Color_HeaderDead, Color("#080808FF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF"));
theme.Set(UI7Color_FrameBackground, Color("#555555FF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
PD_API void Theme::Flashbang(Theme& theme) {
theme.Set(UI7Color_Text, Color("#000000FF"));
theme.Set(UI7Color_TextDead, Color("#333333FF"));
theme.Set(UI7Color_Background, Color("#eeeeeeFF"));
theme.Set(UI7Color_Border, Color("#777777ff"));
theme.Set(UI7Color_Button, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonDead, Color("#bbbbbbFF"));
theme.Set(UI7Color_ButtonActive, Color("#ccccccFF"));
theme.Set(UI7Color_ButtonHovered, Color("#acacacFF"));
theme.Set(UI7Color_Header, Color("#ddddddFF"));
theme.Set(UI7Color_HeaderDead, Color("#cdcdcdFF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#ccccccFF"));
theme.Set(UI7Color_FrameBackground, Color("#aaaaaaFF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#909090FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));
}
} // namespace UI7
} // namespace PD
+92 -96
View File
@@ -41,27 +41,24 @@ PD_API std::string GetVersion(bool show_build) {
}
PD_API void Context::AddViewPort(const ID& id, const ivec4& vp) {
pIO->AddViewPort(id, vp);
pIO.AddViewPort(id, vp);
}
PD_API void Context::UseViewPort(const ID& id) {
if (!pIO->ViewPorts.count(id)) {
if (!pIO.ViewPorts.count(id)) {
return;
}
pIO->CurrentViewPort = pIO->ViewPorts[id];
pIO.CurrentViewPort = pIO.ViewPorts[id.RawID()];
}
PD_API Menu::Ref Context::BeginMenu(const ID& id, UI7MenuFlags flags,
bool* pShow) {
PD_API Menu* Context::BeginMenu(const ID& id, UI7MenuFlags flags, bool* pShow) {
if (pCurrent) {
std::cout << "[UI7] Error: You are already in " << pCurrent->pID.GetName()
<< " Menu" << std::endl;
PDERR("UI7: You are already in {} Menu!", pCurrent->pID.GetName());
return nullptr;
}
if (std::find(pCurrentMenus.begin(), pCurrentMenus.end(), (u32)id) !=
pCurrentMenus.end()) {
std::cout << "[UI7] Error: Menu " << id.GetName() << " already exists!"
<< std::endl;
PDERR("UI7: Menu {} already exists!", id.GetName());
return nullptr;
}
pCurrent = pGetOrCreateMenu(id);
@@ -73,7 +70,7 @@ PD_API Menu::Ref Context::BeginMenu(const ID& id, UI7MenuFlags flags,
}
}
/** Probably we dont even need Input Handling in this stage */
// this->pIO->InputHandler->CurrentMenu = id;
// this->pIO.InputHandler.CurrentMenu = id;
pCurrentMenus.push_back(id);
pCurrent->Flags = flags;
if (!pCurrent->pIsOpen) {
@@ -101,7 +98,7 @@ PD_API void Context::EndMenu() {
return;
}
pCurrent = nullptr;
// pIO->InputHandler->CurrentMenu = 0;
// pIO.InputHandler.CurrentMenu = 0;
}
PD_API void Context::Update() {
@@ -117,41 +114,44 @@ PD_API void Context::Update() {
*
* Very simple ...
*/
pIO->FDL->Clear();
pIO.FDL.Clear();
if (std::find(pCurrentMenus.begin(), pCurrentMenus.end(),
pIO->InputHandler->FocusedMenu) == pCurrentMenus.end()) {
pIO->InputHandler->FocusedMenu = 0;
pIO->InputHandler->FocusedMenuRect = fvec4(0);
pIO.InputHandler.FocusedMenu) == pCurrentMenus.end()) {
pIO.InputHandler.FocusedMenu = 0;
pIO.InputHandler.FocusedMenuRect = fvec4(0);
}
std::vector<u32> FinalList;
for (auto it : pDFO) {
if (std::find(pCurrentMenus.begin(), pCurrentMenus.end(), it) !=
pCurrentMenus.end() &&
it != pIO->InputHandler->FocusedMenu) {
it != pIO.InputHandler.FocusedMenu) {
FinalList.push_back(it);
}
}
for (auto it : pCurrentMenus) {
if (std::find(FinalList.begin(), FinalList.end(), it) == FinalList.end() &&
it != pIO->InputHandler->FocusedMenu) {
it != pIO.InputHandler.FocusedMenu) {
FinalList.push_back(it);
}
}
if (pMenus.count(pIO->InputHandler->FocusedMenu)) {
FinalList.insert(FinalList.begin(), pIO->InputHandler->FocusedMenu);
if (pMenus.count(pIO.InputHandler.FocusedMenu)) {
FinalList.insert(FinalList.begin(), pIO.InputHandler.FocusedMenu);
}
pDFO = FinalList;
for (auto& it : FinalList) {
this->pIO->InputHandler->CurrentMenu = it;
this->pIO.InputHandler.CurrentMenu = it;
pMenus[it]->Update(); /** Render */
this->pIO->InputHandler->CurrentMenu = 0;
this->pIO.InputHandler.CurrentMenu = 0;
}
int base_layer = 0;
for (int i = (int)FinalList.size() - 1; i >= 0; i--) {
pIO->FDL->Merge(pMenus[FinalList[i]]->pLayout->GetDrawList());
pIO.FDL.SetLayer(base_layer);
pIO.FDL.Merge(pMenus[FinalList[i]]->pLayout.GetDrawList());
base_layer += 100;
}
pCurrentMenus.clear();
pIO->Update();
pIO->FDL->pPool.Sort();
pIO.Update();
pIO.FDL.Optimize();
}
PD_API void Context::AboutMenu(bool* show) {
@@ -169,7 +169,7 @@ PD_API void Context::AboutMenu(bool* show) {
m->Label("sizeof(size_t) -> " + std::to_string(sizeof(size_t)));
m->Label("sizeof(LI::Vertex) -> " + std::to_string(sizeof(Li::Vertex)));
m->Label("__cplusplus -> " + std::to_string(__cplusplus));
m->Label("Compiler -> " +
m->Label("Compiler -> {}",
Strings::GetCompilerVersion()); // + LibInfo::CompiledWith());
}
EndMenu();
@@ -182,47 +182,46 @@ PD_API void Context::MetricsMenu(bool* show) {
m->Separator();
m->Label(
std::format("Average {:.3f} ms/f ({:.1f} FPS)",
((float)pIO->DeltaStats->GetAverage() / 1000.f),
1000.f / ((float)pIO->DeltaStats->GetAverage() / 1000.f)));
m->Label(std::format("NumVertices: {}", pIO->NumVertices));
m->Label(std::format("NumIndices: {} -> {} Tris", pIO->NumIndices,
pIO->NumIndices / 3));
((float)pIO.DeltaStats.GetAverage() / 1000.f),
1000.f / ((float)pIO.DeltaStats.GetAverage() / 1000.f)));
m->Label(std::format("NumVertices: {}", pIO.NumVertices));
m->Label(std::format("NumIndices: {} -> {} Tris", pIO.NumIndices,
pIO.NumIndices / 3));
m->Label("Menus: " + std::to_string(pMenus.size()));
/** Section TimeTrace */
m->SeparatorText("TimeTrace");
if (m->BeginTreeNode("Traces (" +
std::to_string(pIO->pCtx.Os()->GetTraceMap().size()) +
")")) {
for (auto& it : pIO->pCtx.Os()->GetTraceMap()) {
if (m->BeginTreeNode(it.second->GetID())) {
m->Label("Diff: " + UI7DTF(it.second->GetLastDiff()));
std::to_string(PD::TT::GetTraceMap().size()) + ")")) {
for (auto& it : PD::TT::GetTraceMap()) {
if (m->BeginTreeNode(it.second.GetID())) {
m->Label("Diff: " + UI7DTF(it.second.GetLastDiff()));
m->Label("Protocol Len: " +
std::to_string(it.second->GetProtocol()->GetLen()));
m->Label("Average: " +
UI7DTF(it.second->GetProtocol()->GetAverage()));
m->Label("Min: " + UI7DTF(it.second->GetProtocol()->GetMin()));
m->Label("Max: " + UI7DTF(it.second->GetProtocol()->GetMax()));
std::to_string(it.second.GetProtocol().GetLen()));
m->Label("Average: " + UI7DTF(it.second.GetProtocol().GetAverage()));
m->Label("Min: " + UI7DTF(it.second.GetProtocol().GetMin()));
m->Label("Max: " + UI7DTF(it.second.GetProtocol().GetMax()));
m->EndTreeNode();
}
}
m->EndTreeNode();
}
m->SeparatorText("Palladium Info");
m->Label("Os Driver: {}", pIO->pCtx.Os()->GetName());
m->Label("Renderer: " + pIO->pCtx.Gfx()->GetName());
if (m->BeginTreeNode(std::string("Input: " + pIO->pCtx.Hid()->GetName()))) {
if (pIO->pCtx.Hid()->Flags & PD::HidDriver::Flags_HasKeyboard) {
m->Label("- Keyboard Supported");
}
if (pIO->pCtx.Hid()->Flags & PD::HidDriver::Flags_HasMouse) {
m->Label("- Mouse Supported");
}
if (pIO->pCtx.Hid()->Flags & PD::HidDriver::Flags_HasTouch) {
m->Label("- Touch Supported");
}
if (pIO->pCtx.Hid()->Flags & PD::HidDriver::FLags_HasGamepad) {
m->Label("- Gamepad Supported");
}
m->Label("Os Driver: {}", PD::Os::GetDriverName());
m->Label("Renderer: {}", PD::Gfx::GetDriverName());
if (m->BeginTreeNode(
std::string(std::string("Input: ") + PD::Hid::GetDriverName()))) {
/* if (pIO.pCtx.Hid()->Flags & PD::HidDriver::Flags_HasKeyboard) {
m->Label("- Keyboard Supported");
}
if (pIO.pCtx.Hid()->Flags & PD::HidDriver::Flags_HasMouse) {
m->Label("- Mouse Supported");
}
if (pIO.pCtx.Hid()->Flags & PD::HidDriver::Flags_HasTouch) {
m->Label("- Touch Supported");
}
if (pIO.pCtx.Hid()->Flags & PD::HidDriver::FLags_HasGamepad) {
m->Label("- Gamepad Supported");
}*/
m->EndTreeNode();
}
/** Section IO */
@@ -231,14 +230,14 @@ PD_API void Context::MetricsMenu(bool* show) {
for (auto& it : pMenus) {
if (m->BeginTreeNode(it.second->pID.GetName())) {
m->Label("Name: " + it.second->pID.GetName());
m->Label(std::format("Pos: {}", it.second->pLayout->GetPosition()));
m->Label(std::format("Size: {}", it.second->pLayout->GetSize()));
m->Label(std::format("WorkRect: {}", it.second->pLayout->WorkRect));
m->Label(std::format("Cursor: {}", it.second->pLayout->Cursor));
m->Label(std::format("Pos: {}", it.second->pLayout.GetPosition()));
m->Label(std::format("Size: {}", it.second->pLayout.GetSize()));
m->Label(std::format("WorkRect: {}", it.second->pLayout.WorkRect));
m->Label(std::format("Cursor: {}", it.second->pLayout.Cursor));
if (m->BeginTreeNode(
"ID Objects (" +
std::to_string(it.second->pLayout->IDObjects.size()) + ")")) {
for (auto& jt : it.second->pLayout->IDObjects) {
std::to_string(it.second->pLayout.IDObjects.size()) + ")")) {
for (auto& jt : it.second->pLayout.IDObjects) {
m->Label(std::format("{:08X}", jt->GetID()));
}
m->EndTreeNode();
@@ -253,15 +252,14 @@ PD_API void Context::MetricsMenu(bool* show) {
for (auto& it : pCurrentMenus) {
if (m->BeginTreeNode(pMenus[it]->pID.GetName())) {
m->Label("Name: " + pMenus[it]->pID.GetName());
m->Label(std::format("Pos: {}", pMenus[it]->pLayout->GetPosition()));
m->Label(std::format("Size: {}", pMenus[it]->pLayout->GetSize()));
m->Label(std::format("WorkRect: {}", pMenus[it]->pLayout->WorkRect));
m->Label(std::format("Cursor: {}", pMenus[it]->pLayout->Cursor));
m->Label(std::format("Pos: {}", pMenus[it]->pLayout.GetPosition()));
m->Label(std::format("Size: {}", pMenus[it]->pLayout.GetSize()));
m->Label(std::format("WorkRect: {}", pMenus[it]->pLayout.WorkRect));
m->Label(std::format("Cursor: {}", pMenus[it]->pLayout.Cursor));
if (m->BeginTreeNode(
"ID Objects (" +
std::to_string(pMenus[it]->pLayout->IDObjects.size()) +
")")) {
for (auto& jt : pMenus[it]->pLayout->IDObjects) {
std::to_string(pMenus[it]->pLayout.IDObjects.size()) + ")")) {
for (auto& jt : pMenus[it]->pLayout.IDObjects) {
m->Label(std::format("{:08X}", jt->GetID()));
}
m->EndTreeNode();
@@ -273,8 +271,8 @@ PD_API void Context::MetricsMenu(bool* show) {
}
// Well this are Li Drawlists now and they do not count their stats (yet)
/*if (m->BeginTreeNode("DrawLists (" +
std::to_string(pIO->DrawListRegestry.size()) + ")")) {
for (auto &it : pIO->DrawListRegestry) {
std::to_string(pIO.DrawListRegestry.size()) + ")")) {
for (auto &it : pIO.DrawListRegestry) {
if (m->BeginTreeNode(it.First.GetName())) {
m->Label("Vertices: " + std::to_string(it.Second->NumVertices));
m->Label("Indices: " + std::to_string(it.Second->NumIndices));
@@ -284,21 +282,20 @@ PD_API void Context::MetricsMenu(bool* show) {
}
m->EndTreeNode();
}*/
m->Label("io->Time: " + Strings::FormatMillis(pIO->Time->Get()));
m->Label(std::format("Delta: {:.3f}", pIO->Delta));
m->Label(std::format("Framerate: {:.2f}", pIO->Framerate));
m->Label("io->Time: " + Strings::FormatMillis(pIO.Time.Get()));
m->Label(std::format("Delta: {:.3f}", pIO.Delta));
m->Label(std::format("Framerate: {:.2f}", pIO.Framerate));
m->Label(std::format("Focused Menu: {:08X}", pIO.InputHandler.FocusedMenu));
m->Label(
std::format("Focused Menu: {:08X}", pIO->InputHandler->FocusedMenu));
m->Label(std::format("Dragged Object: {:08X}",
pIO->InputHandler->DraggedObject));
std::format("Dragged Object: {:08X}", pIO.InputHandler.DraggedObject));
m->Label(std::format("DragTime: {:.2f}s",
pIO->InputHandler->DragTime->GetSeconds()));
m->Label(std::format("DragDestination: [{}]",
pIO->InputHandler->DragDestination));
m->Label(std::format("DragSource: [{}]", pIO->InputHandler->DragSourcePos));
m->Label(std::format("DragPos: [{}]", pIO->InputHandler->DragPosition));
pIO.InputHandler.DragTime.GetSeconds()));
m->Label(
std::format("DragLastPos: [{}]", pIO->InputHandler->DragLastPosition));
std::format("DragDestination: [{}]", pIO.InputHandler.DragDestination));
m->Label(std::format("DragSource: [{}]", pIO.InputHandler.DragSourcePos));
m->Label(std::format("DragPos: [{}]", pIO.InputHandler.DragPosition));
m->Label(
std::format("DragLastPos: [{}]", pIO.InputHandler.DragLastPosition));
EndMenu();
}
}
@@ -307,28 +304,27 @@ PD_API void UI7::Context::StyleEditor(bool* show) {
if (auto m = BeginMenu("UI7 Style Editor", UI7MenuFlags_Scrolling, show)) {
m->Label("Palladium - UI7 " + GetVersion() + " Style Editor");
m->Separator();
m->DragData("MenuPadding", (float*)&pIO->MenuPadding, 2, 0.f, 100.f);
m->DragData("FramePadding", (float*)&pIO->FramePadding, 2, 0.f, 100.f);
m->DragData("ItemSpace", (float*)&pIO->ItemSpace, 2, 0.f, 100.f);
m->DragData("MinSliderSize", (float*)&pIO->MinSliderDragSize, 2, 1.f,
100.f);
m->DragData("OverScroll Modifier", &pIO->OverScrollMod, 1, 0.01f,
m->DragData("MenuPadding", (float*)&pIO.MenuPadding, 2, 0.f, 100.f);
m->DragData("FramePadding", (float*)&pIO.FramePadding, 2, 0.f, 100.f);
m->DragData("ItemSpace", (float*)&pIO.ItemSpace, 2, 0.f, 100.f);
m->DragData("MinSliderSize", (float*)&pIO.MinSliderDragSize, 2, 1.f, 100.f);
m->DragData("OverScroll Modifier", &pIO.OverScrollMod, 1, 0.01f,
std::numeric_limits<float>::max(), 0.01f, 2);
m->Checkbox("Menu Border", pIO->ShowMenuBorder);
m->Checkbox("Frame Border", pIO->ShowFrameBorder);
m->Checkbox("Menu Border", pIO.ShowMenuBorder);
m->Checkbox("Frame Border", pIO.ShowFrameBorder);
m->SeparatorText("Theme");
if (m->Button("Dark")) {
UI7::Theme::Default(*pIO->Theme.get());
UI7::Theme::Default(pIO.Theme);
}
m->SameLine();
if (m->Button("Flashbang")) {
UI7::Theme::Flashbang(*pIO->Theme.get());
UI7::Theme::Flashbang(pIO.Theme);
}
/// Small trick to print without prefix
#define ts(x) m->ColorEdit(std::string(#x).substr(9), pIO->Theme->GetRef(x));
#define ts2(x) \
m->DragData(std::string(#x).substr(9), (u8*)&pIO->Theme->GetRef(x), 4, \
(u8)0, (u8)255);
#define ts(x) m->ColorEdit(std::string(#x).substr(9), pIO.Theme.GetRef(x));
#define ts2(x) \
m->DragData(std::string(#x).substr(9), (u8*)&pIO.Theme.GetRef(x), 4, (u8)0, \
(u8)255);
ts(UI7Color_Background);
ts(UI7Color_Border);
ts(UI7Color_Button);
+114
View File
@@ -0,0 +1,114 @@
#include <pd/ultra/canvas.hpp>
namespace PD {
namespace Ultra {
PD_API Canvas::Canvas() { pRev = 0; }
PD_API Canvas::Canvas(const PD::fvec2& size) : pViewport(size) { pRev = 0; }
PD_API Canvas::~Canvas() {}
PD_API void Canvas::SetVirtualViewport(const PD::fvec2& size) {
pVirtualViewPort = size;
if (pVirtualViewPort.x && pVirtualViewPort.y) {
pVfactor = std::min(pViewport.x / pVirtualViewPort.x,
pViewport.y / pVirtualViewPort.y);
pVoff = (pViewport - (pVirtualViewPort * pVfactor)) * 0.5f;
}
pRev++;
}
PD_API void Canvas::SetViewport(const PD::fvec2& size) {
if (pViewport == size) return;
pViewport = size;
SetVirtualViewport(pVirtualViewPort); // recalculate Vfactor
}
PD_API PD::fvec2 Canvas::VTranslatePos(const PD::fvec2& p) const {
return p * pVfactor + pVoff;
}
PD_API PD::fvec2 Canvas::TranslatePos(const PD::fvec2& p) const {
return p * pViewport;
}
PD_API PD::fvec2 Canvas::VTranslateSize(const PD::fvec2& s) const {
return s * pVfactor;
}
PD_API PD::fvec2 Canvas::TranslateSize(const PD::fvec2& s) const {
return s * pViewport.y;
}
PD_API float Canvas::VTranslateFontscale(float f) const { return f * pVfactor; }
PD_API float Canvas::TranslateFontscale(float f) const {
return f * pViewport.y;
}
PD_API PD::Li::Rect Canvas::VTranslateObject(const PD::fvec2& pos,
const PD::fvec2& size,
UltraAlignment align,
bool size_modified) const {
PD::fvec2 nsize = size;
if (!size_modified) nsize *= pVfactor;
PD::fvec2 final;
if (align & UltraAlignment_Left) {
final.x = pos.x * pVfactor;
} else if (align & UltraAlignment_Right) {
final.x = pViewport.x - (pos.x * pVfactor) - nsize.x;
} else if (align & UltraAlignment_CenterHorizontal) {
final.x = pViewport.x * 0.5 - nsize.x * 0.5 + pos.x * pVfactor;
} else {
final.x = pVoff.x + (pos.x * pVfactor) - nsize.x * 0.5;
}
if (align & UltraAlignment_Top) {
final.y = pos.y * pVfactor;
} else if (align & UltraAlignment_Bot) {
final.y = pViewport.y - (pos.y * pVfactor) - nsize.y;
} else if (align & UltraAlignment_CenterVertical) {
final.y = pViewport.y * 0.5 - nsize.y * 0.5 + pos.y * pVfactor;
} else {
final.y = pVoff.y + (pos.y * pVfactor) - nsize.y * 0.5;
}
return PD::fvec4(final, final + nsize);
}
PD_API PD::Li::Rect Canvas::TranslateObject(const PD::fvec2& pos,
const PD::fvec2& size,
UltraAlignment align,
bool size_modified) const {
PD::fvec2 nsize = size;
if (!size_modified) nsize *= pViewport;
PD::fvec2 final;
if (align & UltraAlignment_Left) {
final.x = pos.x * pViewport.x;
} else if (align & UltraAlignment_Right) {
final.x = pViewport.x - (pos.x * pViewport.x) - nsize.x;
} else if (align & UltraAlignment_CenterHorizontal) {
final.x = pViewport.x * 0.5 - nsize.x * 0.5 + pos.x * pViewport.x;
} else {
final.x = pVoff.x + (pos.x * pViewport.x) - nsize.x * 0.5;
}
if (align & UltraAlignment_Top) {
final.y = pos.y * pViewport.y;
} else if (align & UltraAlignment_Bot) {
final.y = pViewport.y - (pos.y * pViewport.y) - nsize.y;
} else if (align & UltraAlignment_CenterVertical) {
final.y = pViewport.y * 0.5 - nsize.y * 0.5 + pos.y * pViewport.y;
} else {
final.y = pVoff.y + (pos.y * pViewport.y) - nsize.y * 0.5;
}
return PD::fvec4(final, final + nsize);
}
PD_API PD::fvec2 Canvas::VTranslateAlignPos(const PD::fvec2& pos,
const PD::fvec2& size,
UltraAlignment align) const {
return VTranslateObject(pos, size, align).TopLeft();
}
PD_API const PD::u32& Canvas::GetRevision() const { return pRev; }
} // namespace Ultra
} // namespace PD
+46
View File
@@ -0,0 +1,46 @@
#include <pd/drivers/drivers.hpp>
#include <pd/ultra/container.hpp>
#include <pd/ultra/elems/button.hpp>
namespace PD {
namespace Ultra {
PD_API void Button::Draw(PD::Li::Drawlist& l) {
float r = pRounding;
if (pParent) r = pParent->GetCanvas().VTranslateSize(r).x;
l.PathRect(pRenderspace.TopLeft(), pRenderspace.BotRight(), r);
if (pLined) {
l.PathStroke(pRenderColor, pThickness, LiDrawFlags_Close);
} else {
l.PathFill(pRenderColor);
}
if (!pFont) return; // discard here if we dont have a font
PD::fvec2 off = 0.f;
if (pParent) off = pParent->GetCanvas().VTranslateSize(pAsp) * 0.5;
l.SetFont(pFont);
l.SetFontscale(pParent->GetCanvas().VTranslateFontscale(pFontScale));
l.DrawText(pRenderspace.TopLeft() + off, pText.c_str(), pTextColor);
}
PD_API void Button::Update() {
pRenderColor = pColor;
if (!pParent || !pFont) ElementBase::Update();
PD::fvec2 size = pSize;
if (size == PD::fvec2(0)) {
size = pFont->GetTextBounds(
pText.c_str(),
pParent->GetCanvas().VTranslateFontscale(pFontScale)) +
pParent->GetCanvas().VTranslateSize(pAsp);
} else {
size = pParent->GetCanvas().VTranslateSize(size);
}
pRenderspace = pParent->GetCanvas().VTranslateObject(
pParent->GetTopLeft() + pPos, size, pAlignment, true);
if (PD::Li::Math::InSpace(PD::Hid::MousePos(), pRenderspace)) {
pRenderColor = pHovered;
} else {
pRenderColor = pColor;
}
UpdateInput();
}
} // namespace Ultra
} // namespace PD
+47
View File
@@ -0,0 +1,47 @@
#include <pd/drivers/drivers.hpp>
#include <pd/ultra/container.hpp>
#include <pd/ultra/elems/element.hpp>
namespace PD {
namespace Ultra {
PD_API bool ElementBase::RevisionUpdate(PD::u32 req) {
if (req != pCanvasRev) {
pCanvasRev = req;
return true;
} else {
return false;
}
}
PD_API void ElementBase::Update() {
if (!pParent)
pRenderspace = PD::fvec4(pPos, pPos + pSize);
else
pRenderspace = pParent->GetCanvas().VTranslateObject(
pParent->GetTopLeft() + pPos, pSize, pAlignment);
UpdateInput();
/*pRenderspace = PD::fvec4(pParent->GetTopLeft() + pPos,
pParent->GetTopLeft() + pPos + pSize);*/
}
PD_API void ElementBase::UpdateInput() {
if (PD::Li::Math::InSpace(
PD::Hid::MousePos(),
PD::fvec4(pRenderspace.TopLeft(), pRenderspace.BotRight()))) {
if (pHover && !pFocued) {
pHover();
pFocued = true;
}
if (PD::Hid::IsEvent(PD::Hid::Event::Up, PD::Hid::Gamepad::Touch)) {
if (pPress) {
pPress();
}
}
} else {
if (pUnHover && pFocued) {
pUnHover();
pFocued = false;
}
}
}
} // namespace Ultra
} // namespace PD
+11
View File
@@ -0,0 +1,11 @@
#include <pd/ultra/elems/image.hpp>
namespace PD {
namespace Ultra {
PD_API void Image::Draw(PD::Li::Drawlist& l) {
l.BindTexture(*pTex);
l.PathRect(pRenderspace.TopLeft(), pRenderspace.BotRight(), pRounding);
l.PathFill(pColor);
}
} // namespace Ultra
} // namespace PD
+14
View File
@@ -0,0 +1,14 @@
#include <pd/ultra/elems/rect.hpp>
namespace PD {
namespace Ultra {
PD_API void Rect::Draw(PD::Li::Drawlist& l) {
l.PathRect(pRenderspace.TopLeft(), pRenderspace.BotRight(), pRounding);
if (pLined) {
l.PathStroke(pColor, pThickness, LiDrawFlags_Close);
} else {
l.PathFill(pColor);
}
}
} // namespace Ultra
} // namespace PD
+23
View File
@@ -0,0 +1,23 @@
#include <pd/ultra/container.hpp>
#include <pd/ultra/elems/text.hpp>
namespace PD {
namespace Ultra {
PD_API void Text::Draw(PD::Li::Drawlist& l) {
if (!pFont) return;
l.SetFont(pFont);
l.SetFontscale(pParent->GetCanvas().VTranslateFontscale(pFontScale));
l.DrawText(pRenderspace.TopLeft(), pText.c_str(), pColor);
}
PD_API void Text::Update() {
if (!pFont) return;
if (!pParent) ElementBase::Update();
pRenderspace = pParent->GetCanvas().VTranslateObject(
pParent->GetTopLeft() + pPos,
pFont->GetTextBounds(
pText.c_str(), pParent->GetCanvas().VTranslateFontscale(pFontScale)),
pAlignment, true);
}
} // namespace Ultra
} // namespace PD
+19
View File
@@ -0,0 +1,19 @@
#include <pd/lithium/formatters.hpp>
#include <pd/ultra/layout.hpp>
namespace PD {
namespace Ultra {
PD_API void Layout::Render(PD::Li::Drawlist& list) {
float fc = list.GetFontScale();
list.SetFontscale(GetCanvas().VTranslateFontscale(fc));
for (auto& it : GetElements()) {
it->SetFontIfNull(*pFont);
it->Update();
it->Draw(list);
}
list.DrawText(PD::fvec2(5, GetBotLeft().y - 40),
std::format("Lyt: [{}]", GetRenderspace()).c_str(), 0xffff00ff);
list.SetFontscale(fc);
}
} // namespace Ultra
} // namespace PD