add ImageData.drawLine
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
||||||
@@ -93,6 +94,99 @@ void ImageData::blit(const ImageData* image, int x, int y) {
|
|||||||
throw std::runtime_error("mismatching format");
|
throw std::runtime_error("mismatching format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint channels>
|
||||||
|
static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const glm::ivec4& color) {
|
||||||
|
ubyte* data = image.getData();
|
||||||
|
uint width = image.getWidth();
|
||||||
|
uint height = image.getHeight();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dx = std::abs(x2 - x1);
|
||||||
|
int dy = -std::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] = color[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color) {
|
||||||
|
switch (format) {
|
||||||
|
case ImageFormat::rgb888:
|
||||||
|
draw_line<3>(*this, x1, y1, x2, y2, color);
|
||||||
|
break;
|
||||||
|
case ImageFormat::rgba8888:
|
||||||
|
draw_line<4>(*this, x1, y1, x2, y2, color);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) {
|
void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) {
|
||||||
ubyte* source = image->getData();
|
ubyte* source = image->getData();
|
||||||
uint srcwidth = image->getWidth();
|
uint srcwidth = image->getWidth();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
enum class ImageFormat {
|
enum class ImageFormat {
|
||||||
@@ -23,6 +24,7 @@ public:
|
|||||||
void flipX();
|
void flipX();
|
||||||
void flipY();
|
void flipY();
|
||||||
|
|
||||||
|
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
|
||||||
void blitRGB_on_RGBA(const ImageData* image, int x, int y);
|
void blitRGB_on_RGBA(const ImageData* image, int x, int y);
|
||||||
void blitMatchingFormat(const ImageData* image, int x, int y);
|
void blitMatchingFormat(const ImageData* image, int x, int y);
|
||||||
void blit(const ImageData* image, int x, int y);
|
void blit(const ImageData* image, int x, int y);
|
||||||
@@ -44,6 +46,11 @@ public:
|
|||||||
uint getHeight() const {
|
uint getHeight() const {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t getDataSize() const {
|
||||||
|
size_t channels = 3 + (format == ImageFormat::rgba8888);
|
||||||
|
return width * height * channels;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<ImageData> add_atlas_margins(ImageData* image, int grid_size);
|
std::unique_ptr<ImageData> add_atlas_margins(ImageData* image, int grid_size);
|
||||||
|
|||||||
@@ -80,65 +80,26 @@ static int l_set(State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_clear(State* L) {
|
static int l_clear(State* L) {
|
||||||
RGBA rgba {};
|
auto canvas = touserdata<LuaCanvas>(L, 1);
|
||||||
if (gettop(L) > 1) {
|
if (canvas == nullptr) {
|
||||||
rgba = get_rgba(L, 2);
|
throw std::runtime_error("used canvas.clear instead of canvas:clear");
|
||||||
}
|
}
|
||||||
if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
|
auto& image = canvas->data();
|
||||||
auto& image = canvas->data();
|
ubyte* data = image.getData();
|
||||||
ubyte* data = image.getData();
|
RGBA rgba {};
|
||||||
size_t pixels = image.getWidth() * image.getHeight();
|
if (gettop(L) == 1) {
|
||||||
const size_t channels = 4;
|
std::fill(data, data + image.getDataSize(), 0);
|
||||||
for (size_t i = 0; i < pixels * channels; i++) {
|
return 0;
|
||||||
data[i] = rgba.arr[i % channels];
|
}
|
||||||
}
|
rgba = get_rgba(L, 2);
|
||||||
|
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;
|
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) {
|
static int l_line(State* L) {
|
||||||
int x1 = tointeger(L, 2);
|
int x1 = tointeger(L, 2);
|
||||||
int y1 = tointeger(L, 3);
|
int y1 = tointeger(L, 3);
|
||||||
@@ -149,40 +110,9 @@ static int l_line(State* L) {
|
|||||||
RGBA rgba = get_rgba(L, 6);
|
RGBA rgba = get_rgba(L, 6);
|
||||||
if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
|
if (auto canvas = touserdata<LuaCanvas>(L, 1)) {
|
||||||
auto& image = canvas->data();
|
auto& image = canvas->data();
|
||||||
ubyte* data = image.getData();
|
image.drawLine(
|
||||||
uint width = image.getWidth();
|
x1, y1, x2, y2, glm::ivec4 {rgba.r, rgba.g, rgba.b, rgba.a}
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -197,10 +127,9 @@ static int l_update(State* L) {
|
|||||||
static std::unordered_map<std::string, lua_CFunction> methods {
|
static std::unordered_map<std::string, lua_CFunction> methods {
|
||||||
{"at", lua::wrap<l_at>},
|
{"at", lua::wrap<l_at>},
|
||||||
{"set", lua::wrap<l_set>},
|
{"set", lua::wrap<l_set>},
|
||||||
{"clear", lua::wrap<l_clear>},
|
|
||||||
{"line", lua::wrap<l_line>},
|
{"line", lua::wrap<l_line>},
|
||||||
{"update", lua::wrap<l_update>}
|
{"clear", lua::wrap<l_clear>},
|
||||||
};
|
{"update", lua::wrap<l_update>}};
|
||||||
|
|
||||||
static int l_meta_index(State* L) {
|
static int l_meta_index(State* L) {
|
||||||
auto texture = touserdata<LuaCanvas>(L, 1);
|
auto texture = touserdata<LuaCanvas>(L, 1);
|
||||||
@@ -237,9 +166,7 @@ static int l_meta_newindex(State* L) {
|
|||||||
if (isnumber(L, 2) && isnumber(L, 3)) {
|
if (isnumber(L, 2) && isnumber(L, 3)) {
|
||||||
if (auto pixel = get_at(data, static_cast<uint>(tointeger(L, 2)))) {
|
if (auto pixel = get_at(data, static_cast<uint>(tointeger(L, 2)))) {
|
||||||
pixel->rgba = static_cast<uint>(tointeger(L, 3));
|
pixel->rgba = static_cast<uint>(tointeger(L, 3));
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user