add canvas:clear(...), canvas:line(...)

This commit is contained in:
MihailRis 2025-03-07 20:15:59 +03:00
parent ee3fbc6831
commit dca4f283cc

View File

@ -17,6 +17,7 @@ union RGBA {
struct {
uint8_t r, g, b, a;
};
uint8_t arr[4];
uint32_t rgba;
};
@ -49,32 +50,140 @@ static int l_at(State* L) {
return 0;
}
static RGBA get_rgba(State* L, int first) {
RGBA rgba {};
rgba.a = 255;
switch (gettop(L) - first) {
case 0:
rgba.rgba = static_cast<uint>(tointeger(L, first));
break;
case 3:
rgba.a = static_cast<ubyte>(tointeger(L, first + 3));
[[fallthrough]];
case 2:
rgba.r = static_cast<ubyte>(tointeger(L, first));
rgba.g = static_cast<ubyte>(tointeger(L, first + 1));
rgba.b = static_cast<ubyte>(tointeger(L, first + 2));
break;
}
return rgba;
}
static int l_set(State* L) {
auto x = static_cast<uint>(tointeger(L, 2));
auto y = static_cast<uint>(tointeger(L, 3));
if (auto pixel = get_at(L, x, y)) {
switch (gettop(L)) {
case 4:
pixel->rgba = static_cast<uint>(tointeger(L, 4));
return 1;
case 6:
pixel->r = static_cast<ubyte>(tointeger(L, 4));
pixel->g = static_cast<ubyte>(tointeger(L, 5));
pixel->b = static_cast<ubyte>(tointeger(L, 6));
pixel->a = 255;
return 1;
case 7:
pixel->r = static_cast<ubyte>(tointeger(L, 4));
pixel->g = static_cast<ubyte>(tointeger(L, 5));
pixel->b = static_cast<ubyte>(tointeger(L, 6));
pixel->a = static_cast<ubyte>(tointeger(L, 7));
return 1;
default:
return 0;
*pixel = get_rgba(L, 4);
}
return 0;
}
static int l_clear(State* L) {
RGBA rgba {};
if (gettop(L) > 1) {
rgba = get_rgba(L, 2);
}
if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
auto& image = canvas->data();
ubyte* data = image.getData();
size_t pixels = image.getWidth() * image.getHeight();
const size_t channels = 4;
for (size_t i = 0; i < pixels * channels; i++) {
data[i] = rgba.arr[i % channels];
}
}
return 0;
}
bool clip_line(int& x1, int& y1, int& x2, int& y2, int width, int height) {
const int left = 0;
const int right = width;
const int bottom = 0;
const int top = height;
int dx = x2 - x1;
int dy = y2 - y1;
float t0 = 0.0f;
float t1 = 1.0f;
auto clip = [](int p, int q, float& t0, float& t1) {
if (p == 0) {
return q >= 0;
}
float t = static_cast<float>(q) / p;
if (p < 0) {
if (t > t1) return false;
if (t > t0) t0 = t;
} else {
if (t < t0) return false;
if (t < t1) t1 = t;
}
return true;
};
if (!clip(-dx, x1 - left, t0, t1)) return false;
if (!clip( dx, right - x1, t0, t1)) return false;
if (!clip(-dy, y1 - bottom, t0, t1)) return false;
if (!clip( dy, top - y1, t0, t1)) return false;
if (t1 < 1.0f) {
x2 = x1 + static_cast<int>(std::round(t1 * dx));
y2 = y1 + static_cast<int>(std::round(t1 * dy));
}
if (t0 > 0.0f) {
x1 = x1 + static_cast<int>(std::round(t0 * dx));
y1 = y1 + static_cast<int>(std::round(t0 * dy));
}
return true;
}
static int l_line(State* L) {
int x1 = tointeger(L, 2);
int y1 = tointeger(L, 3);
int x2 = tointeger(L, 4);
int y2 = tointeger(L, 5);
RGBA rgba = get_rgba(L, 6);
if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
auto& image = canvas->data();
ubyte* data = image.getData();
uint width = image.getWidth();
uint height = image.getHeight();
const uint channels = 4;
if ((x1 < 0 || x1 >= width || x2 < 0 || x2 >= width ||
y1 < 0 || y1 >= height || y2 < 0 || y2 >= height) &&
!clip_line(x1, y1, x2, y2, width, height)) {
return 0;
}
int dx = glm::abs(x2 - x1);
int dy = -glm::abs(y2 - y1);
int sx = x1 < x2 ? 1 : -1;
int sy = y1 < y2 ? 1 : -1;
int err = dx + dy;
while (true) {
size_t pos = (y1 * width + x1) * channels;
for (int i = 0; i < channels; i++) {
data[pos + i] = rgba.arr[i];
}
if (x1 == x2 && y1 == y2) break;
int e2 = 2 * err;
if (e2 >= dy) {
err += dy;
x1 += sx;
}
if (e2 <= dx) {
err += dx;
y1 += sy;
}
}
}
return 0;
}
@ -88,6 +197,8 @@ static int l_update(State* L) {
static std::unordered_map<std::string, lua_CFunction> methods {
{"at", lua::wrap<l_at>},
{"set", lua::wrap<l_set>},
{"clear", lua::wrap<l_clear>},
{"line", lua::wrap<l_line>},
{"update", lua::wrap<l_update>}
};