Merge dev-0.7-rewrite into stable
This commit is contained in:
@@ -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
|
||||
Executable → Regular
+38
-38
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
Vendored
-27
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user