Add support for rotated gradients

This commit is contained in:
2026-04-18 14:33:31 +02:00
parent 6dbf5a4812
commit b99fc39444
5 changed files with 136 additions and 2 deletions

View File

@@ -121,6 +121,20 @@ class PD_API Color {
return *this;
}
/**
* Lerp
* @param v Target color
* @param t interpolation factor
* @return Class Reference
*/
constexpr Color& Lerp(const Color& v, float t) {
a = static_cast<u8>(a + (v.a - a) * t);
b = static_cast<u8>(b + (v.b - b) * t);
g = static_cast<u8>(g + (v.g - g) * t);
r = static_cast<u8>(r + (v.r - r) * t);
return *this;
}
/**
* Get 32Bit Color Value
* @return 32Bit Color Value (ABGR iirc)

View File

@@ -26,6 +26,7 @@ using LiDrawFlags = PD::u32;
enum LiDrawFlags_ : PD::u32 {
LiDrawFlags_None = 0,
LiDrawFlags_Close = 1 << 0,
LiDrawFlags_AA = 1 << 1,
};
namespace PD {
@@ -58,6 +59,8 @@ class PD_API Drawlist {
void PathStroke(const PD::Color& color, int t = 1,
LiDrawFlags flags = LiDrawFlags_None);
void PathFill(const PD::Color& color);
void PathFillGradient(const PD::Color& a, const PD::Color& b,
float rad = 0.f);
void PathArcToN(const fvec2& c, float r, float amin, float amax, int s);
void PathFastArcToN(const fvec2& c, float r, float amin, float amax, int s);
void PathRect(const fvec2& tl, const fvec2& br, float r = 0.f);
@@ -97,6 +100,8 @@ class PD_API Drawlist {
void DrawPolyLine(const Pool<fvec2>& points, const PD::Color& color,
LiDrawFlags flags = LiDrawFlags_None, int t = 1);
void DrawConvexPolyFilled(const Pool<fvec2>& points, const PD::Color& color);
void DrawConvexPolyFilled(const Pool<fvec2>& points, const PD::Color& a,
const PD::Color b, float rad = 0.f);
void PrimQuad(Command& cmd, const Rect& quad, const Rect& uv,
const PD::Color& color);

View File

@@ -45,6 +45,12 @@ PD_API void Drawlist::PathFill(const PD::Color& 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);
@@ -229,9 +235,9 @@ PD_API void Drawlist::DrawPolyLine(const Pool<fvec2>& points,
}
UnbindTexture();
auto& cmd = NewCommand();
bool close = (flags & (1 << 0));
bool close = (flags & LiDrawFlags_Close);
int num_points = close ? (int)points.size() : (int)points.size() - 1;
if (flags & (1 << 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
@@ -282,6 +288,57 @@ PD_API void Drawlist::DrawConvexPolyFilled(const Pool<fvec2>& points,
}
}
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);

View File

@@ -58,6 +58,7 @@ 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);

View File

@@ -166,6 +166,52 @@ struct Cursor {
}
};
void DrawRectGradient135(PD::Li::Drawlist& l, const PD::fvec2& pos,
const PD::fvec2& size, const PD::Color& colA,
const PD::Color& colB) {
PD::fvec2 p0 = pos;
PD::fvec2 p1 = PD::fvec2(pos.x + size.x, pos.y);
PD::fvec2 p2 = PD::fvec2(pos.x + size.x, pos.y + size.y);
PD::fvec2 p3 = PD::fvec2(pos.x, pos.y + size.y);
// 135° direction
PD::fvec2 dir = PD::fvec2(-0.70710678f, 0.70710678f);
// Project all corners
float t0 = p0.x * dir.x + p0.y * dir.y;
float t1 = p1.x * dir.x + p1.y * dir.y;
float t2 = p2.x * dir.x + p2.y * dir.y;
float t3 = p3.x * dir.x + p3.y * dir.y;
float tmin = std::min({t0, t1, t2, t3});
float tmax = std::max({t0, t1, t2, t3});
auto normalize = [&](float t) { return (t - tmin) / (tmax - tmin); };
auto lerpColor = [&](float t) {
return PD::Color(colA.rf() + (colB.rf() - colA.rf()) * t,
colA.gf() + (colB.gf() - colA.gf()) * t,
colA.bf() + (colB.bf() - colA.bf()) * t,
colA.af() + (colB.af() - colA.af()) * t);
};
PD::Color c0 = lerpColor(normalize(t0));
PD::Color c1 = lerpColor(normalize(t1));
PD::Color c2 = lerpColor(normalize(t2));
PD::Color c3 = lerpColor(normalize(t3));
auto& cmd = l.NewCommand();
cmd.Reserve(4, 6);
cmd.Add(2, 1, 0);
cmd.Add(3, 2, 0);
cmd.Add(PD::Li::Vertex(p0, PD::fvec2(0, 0), c0));
cmd.Add(PD::Li::Vertex(p1, PD::fvec2(1, 0), c1));
cmd.Add(PD::Li::Vertex(p2, PD::fvec2(1, 1), c2));
cmd.Add(PD::Li::Vertex(p3, PD::fvec2(0, 1), c3));
}
int main(int argc, char** argv) {
// PD::LogFilter(PD::LogLevel::Warning);
Driver drv = Driver::OpenGL3;
@@ -252,6 +298,17 @@ int main(int argc, char** argv) {
"#ffffff");
LeftStick.Render(pList);
RightStick.Render(pList);
pList.UnbindTexture();
pList.PathRect(50, PD::fvec2(450, 240));
pList.PathFillGradient("#ff0000", "#990000", PD::Radians(135));
pList.PathAdd(PD::fvec2(100, 120));
pList.PathAdd(PD::fvec2(250, 260));
pList.PathAdd(PD::fvec2(420, 180));
pList.PathAdd(PD::fvec2(600, 320));
pList.PathAdd(PD::fvec2(820, 220));
pList.PathAdd(PD::fvec2(1000, 360));
pList.PathStroke("#ff00ff", 10, LiDrawFlags_AA);
PD::Gfx::Reset();
PD::Gfx::Draw(pList);
pList.Clear();