add color32

This commit is contained in:
blueloveTH 2025-04-17 14:41:38 +08:00
parent b12d2bc4cc
commit cfa909f1b9
6 changed files with 348 additions and 0 deletions

View File

@ -33,3 +33,13 @@ typedef union c11_mat3x3 {
float m[3][3]; float m[3][3];
float data[9]; float data[9];
} c11_mat3x3; } c11_mat3x3;
typedef union c11_color32 {
struct {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
unsigned char data[4];
} c11_color32;

View File

@ -19,6 +19,7 @@ typedef struct py_TValue {
PyObject* _obj; PyObject* _obj;
c11_vec2 _vec2; c11_vec2 _vec2;
c11_vec2i _vec2i; c11_vec2i _vec2i;
c11_color32 _color32;
void* _ptr; void* _ptr;
}; };
} py_TValue; } py_TValue;

View File

@ -672,12 +672,14 @@ void py_newvec2(py_OutRef out, c11_vec2);
void py_newvec3(py_OutRef out, c11_vec3); void py_newvec3(py_OutRef out, c11_vec3);
void py_newvec2i(py_OutRef out, c11_vec2i); void py_newvec2i(py_OutRef out, c11_vec2i);
void py_newvec3i(py_OutRef out, c11_vec3i); void py_newvec3i(py_OutRef out, c11_vec3i);
void py_newcolor32(py_OutRef out, c11_color32);
c11_mat3x3* py_newmat3x3(py_OutRef out); c11_mat3x3* py_newmat3x3(py_OutRef out);
c11_vec2 py_tovec2(py_Ref self); c11_vec2 py_tovec2(py_Ref self);
c11_vec3 py_tovec3(py_Ref self); c11_vec3 py_tovec3(py_Ref self);
c11_vec2i py_tovec2i(py_Ref self); c11_vec2i py_tovec2i(py_Ref self);
c11_vec3i py_tovec3i(py_Ref self); c11_vec3i py_tovec3i(py_Ref self);
c11_mat3x3* py_tomat3x3(py_Ref self); c11_mat3x3* py_tomat3x3(py_Ref self);
c11_color32 py_tocolor32(py_Ref self);
/************* Others *************/ /************* Others *************/
@ -765,6 +767,7 @@ enum py_PredefinedType {
tp_vec2i, tp_vec2i,
tp_vec3i, tp_vec3i,
tp_mat3x3, tp_mat3x3,
tp_color32,
/* array2d */ /* array2d */
tp_array2d_like, tp_array2d_like,
tp_array2d_like_iterator, tp_array2d_like_iterator,

View File

@ -180,4 +180,44 @@ class vec3(_vecF['vec3']):
def __init__(self, xyz: vec3i) -> None: ... def __init__(self, xyz: vec3i) -> None: ...
# Color32
class color32:
def __new__(cls, r: int, g: int, b: int, a: int) -> 'color32': ...
def __eq__(self, other: object) -> bool: ...
def __ne__(self, other: object) -> bool: ...
def __repr__(self) -> str: ...
@property
def r(self) -> int: ...
@property
def g(self) -> int: ...
@property
def b(self) -> int: ...
@property
def a(self) -> int: ...
def with_r(self, r: int) -> 'color32': ...
def with_g(self, g: int) -> 'color32': ...
def with_b(self, b: int) -> 'color32': ...
def with_a(self, a: int) -> 'color32': ...
@staticmethod
def from_hex(hex: str) -> 'color32': ...
@staticmethod
def from_vec3(vec: vec3) -> 'color32': ...
@staticmethod
def from_vec3i(vec: vec3i) -> 'color32': ...
def to_hex(self) -> str: ...
def to_vec3(self) -> vec3: ...
def to_vec3i(self) -> vec3i: ...
def ansi_fg(self, text: str) -> str: ...
def ansi_bg(self, text: str) -> str: ...
@staticmethod
def alpha_blend(src: color32, dst: color32) -> color32: ...
def rgb(r: int, g: int, b: int) -> color32: ...
def rgba(r: int, g: int, b: int, a: float) -> color32: ...

View File

@ -1,3 +1,4 @@
#include "pocketpy/linalg.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
@ -836,6 +837,246 @@ static bool vec3__with_xy(int argc, py_Ref argv) {
return true; return true;
} }
/* Color32 */
void py_newcolor32(py_OutRef out, c11_color32 color) {
out->type = tp_color32;
out->is_ptr = false;
out->_color32 = color;
}
c11_color32 py_tocolor32(py_Ref obj) {
assert(obj->type == tp_color32);
return obj->_color32;
}
static bool color32__new__(int argc, py_Ref argv) {
PY_CHECK_ARGC(5);
c11_color32 color;
for(int i = 1; i < 5; i++) {
PY_CHECK_ARG_TYPE(i, tp_int);
py_i64 val = py_toint(&argv[i]);
if(val < 0 || val > 255) return ValueError("color32 values must be between 0 and 255");
color.data[i - 1] = (unsigned char)val;
}
py_newcolor32(py_retval(), color);
return true;
}
#define DEFINE_COLOR32_FIELD(name) \
static bool color32__##name(int argc, py_Ref argv) { \
PY_CHECK_ARGC(1); \
c11_color32 color = py_tocolor32(argv); \
py_newint(py_retval(), color.name); \
return true; \
} \
static bool color32_with_##name(int argc, py_Ref argv) { \
PY_CHECK_ARGC(2); \
c11_color32 color = py_tocolor32(argv); \
PY_CHECK_ARG_TYPE(1, tp_int); \
py_i64 val = py_toint(&argv[1]); \
if(val < 0 || val > 255) { \
return ValueError("color32 values must be between 0 and 255"); \
} \
color.name = (unsigned char)val; \
py_newcolor32(py_retval(), color); \
return true; \
}
DEFINE_COLOR32_FIELD(r)
DEFINE_COLOR32_FIELD(g)
DEFINE_COLOR32_FIELD(b)
DEFINE_COLOR32_FIELD(a)
#undef DEFINE_COLOR32_FIELD
static bool color32_from_hex_STATIC(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_str);
c11_sv hex = py_tosv(argv);
c11_color32 color;
int res;
if(hex.size == 7) {
res = scanf(hex.data, "#%2hhx%2hhx%2hhx", &color.r, &color.g, &color.b);
if(res != 3) return ValueError("invalid hex color format");
color.a = 255;
} else {
res = sscanf(hex.data, "#%2hhx%2hhx%2hhx%2hhx", &color.r, &color.g, &color.b, &color.a);
if(res != 4) return ValueError("invalid hex color format");
}
py_newcolor32(py_retval(), color);
return true;
}
static bool color32_from_vec3_STATIC(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_vec3);
c11_vec3 v = py_tovec3(argv);
c11_color32 color;
color.r = (unsigned char)(v.x * 255);
color.g = (unsigned char)(v.y * 255);
color.b = (unsigned char)(v.z * 255);
color.a = 255;
py_newcolor32(py_retval(), color);
return true;
}
static bool color32_from_vec3i_STATIC(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_vec3i);
c11_vec3i v = py_tovec3i(argv);
c11_color32 color;
color.r = (unsigned char)v.x;
color.g = (unsigned char)v.y;
color.b = (unsigned char)v.z;
color.a = 255;
py_newcolor32(py_retval(), color);
return true;
}
static bool color32_to_hex(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
char buf[16];
int size;
if(color.a == 255) {
size = snprintf(buf, sizeof(buf), "#%02x%02x%02x", color.r, color.g, color.b);
} else {
size = snprintf(buf, sizeof(buf), "#%02x%02x%02x%02x", color.r, color.g, color.b, color.a);
}
py_newstrv(py_retval(), (c11_sv){buf, size});
return true;
}
static void c11_color32_premult(c11_color32* color) {
if(color->a == 255) return;
float alpha = color->a / 255.0f;
color->r = (unsigned char)(color->r * alpha);
color->g = (unsigned char)(color->g * alpha);
color->b = (unsigned char)(color->b * alpha);
}
static bool color32_to_vec3(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
c11_color32_premult(&color);
c11_vec3 v;
v.x = (float)color.r / 255;
v.y = (float)color.g / 255;
v.z = (float)color.b / 255;
py_newvec3(py_retval(), v);
return true;
}
static bool color32_to_vec3i(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
c11_color32_premult(&color);
c11_vec3i v;
v.x = (int)color.r;
v.y = (int)color.g;
v.z = (int)color.b;
py_newvec3i(py_retval(), v);
return true;
}
static bool color32__eq__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
if(argv[1].type != tp_color32) {
py_newnotimplemented(py_retval());
return true;
}
c11_color32 lhs = py_tocolor32(&argv[0]);
c11_color32 rhs = py_tocolor32(&argv[1]);
bool eq = memcmp(&lhs, &rhs, sizeof(c11_color32)) == 0;
py_newbool(py_retval(), eq);
return true;
}
static bool color32__ne__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
if(argv[1].type != tp_color32) {
py_newnotimplemented(py_retval());
return true;
}
c11_color32 lhs = py_tocolor32(&argv[0]);
c11_color32 rhs = py_tocolor32(&argv[1]);
bool eq = memcmp(&lhs, &rhs, sizeof(c11_color32)) != 0;
py_newbool(py_retval(), eq);
return true;
}
static bool color32__repr__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
char buf[64];
int size = snprintf(buf, 64, "color32(%d, %d, %d, %d)", color.r, color.g, color.b, color.a);
py_newstrv(py_retval(), (c11_sv){buf, size});
return true;
}
static bool color32_ansi_fg(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
c11_color32_premult(&color);
char buf[32];
int size = snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", color.r, color.g, color.b);
py_newstrv(py_retval(), (c11_sv){buf, size});
return true;
}
static bool color32_ansi_bg(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
c11_color32_premult(&color);
char buf[32];
int size = snprintf(buf, sizeof(buf), "\033[48;2;%d;%d;%dm", color.r, color.g, color.b);
py_newstrv(py_retval(), (c11_sv){buf, size});
return true;
}
static bool linalg_rgb(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
PY_CHECK_ARG_TYPE(0, tp_int);
PY_CHECK_ARG_TYPE(1, tp_int);
PY_CHECK_ARG_TYPE(2, tp_int);
c11_color32 color;
color.r = (unsigned char)py_toint(&argv[0]);
color.g = (unsigned char)py_toint(&argv[1]);
color.b = (unsigned char)py_toint(&argv[2]);
color.a = 255;
py_newcolor32(py_retval(), color);
return true;
}
static bool linalg_rgba(int argc, py_Ref argv) {
PY_CHECK_ARGC(4);
PY_CHECK_ARG_TYPE(0, tp_int);
PY_CHECK_ARG_TYPE(1, tp_int);
PY_CHECK_ARG_TYPE(2, tp_int);
PY_CHECK_ARG_TYPE(3, tp_float);
c11_color32 color;
color.r = (unsigned char)py_toint(&argv[0]);
color.g = (unsigned char)py_toint(&argv[1]);
color.b = (unsigned char)py_toint(&argv[2]);
color.a = (unsigned char)(py_tofloat(&argv[3]) * 255);
py_newcolor32(py_retval(), color);
return true;
}
static bool color32_alpha_blend_STATIC(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_color32 src = py_tocolor32(&argv[0]);
c11_color32 dst = py_tocolor32(&argv[1]);
float alpha = src.a / 255.0f;
c11_color32 res;
res.r = (unsigned char)(src.r * alpha + dst.r * (1 - alpha));
res.g = (unsigned char)(src.g * alpha + dst.g * (1 - alpha));
res.b = (unsigned char)(src.b * alpha + dst.b * (1 - alpha));
res.a = (unsigned char)(src.a * alpha + dst.a * (1 - alpha));
py_newcolor32(py_retval(), res);
return true;
}
void pk__add_module_linalg() { void pk__add_module_linalg() {
py_Ref mod = py_newmodule("linalg"); py_Ref mod = py_newmodule("linalg");
@ -844,18 +1085,21 @@ void pk__add_module_linalg() {
py_Type vec2i = pk_newtype("vec2i", tp_object, mod, NULL, false, true); py_Type vec2i = pk_newtype("vec2i", tp_object, mod, NULL, false, true);
py_Type vec3i = pk_newtype("vec3i", tp_object, mod, NULL, false, true); py_Type vec3i = pk_newtype("vec3i", tp_object, mod, NULL, false, true);
py_Type mat3x3 = pk_newtype("mat3x3", tp_object, mod, NULL, false, true); py_Type mat3x3 = pk_newtype("mat3x3", tp_object, mod, NULL, false, true);
py_Type color32 = pk_newtype("color32", tp_object, mod, NULL, false, true);
py_setdict(mod, py_name("vec2"), py_tpobject(vec2)); py_setdict(mod, py_name("vec2"), py_tpobject(vec2));
py_setdict(mod, py_name("vec3"), py_tpobject(vec3)); py_setdict(mod, py_name("vec3"), py_tpobject(vec3));
py_setdict(mod, py_name("vec2i"), py_tpobject(vec2i)); py_setdict(mod, py_name("vec2i"), py_tpobject(vec2i));
py_setdict(mod, py_name("vec3i"), py_tpobject(vec3i)); py_setdict(mod, py_name("vec3i"), py_tpobject(vec3i));
py_setdict(mod, py_name("mat3x3"), py_tpobject(mat3x3)); py_setdict(mod, py_name("mat3x3"), py_tpobject(mat3x3));
py_setdict(mod, py_name("color32"), py_tpobject(color32));
assert(vec2 == tp_vec2); assert(vec2 == tp_vec2);
assert(vec3 == tp_vec3); assert(vec3 == tp_vec3);
assert(vec2i == tp_vec2i); assert(vec2i == tp_vec2i);
assert(vec3i == tp_vec3i); assert(vec3i == tp_vec3i);
assert(mat3x3 == tp_mat3x3); assert(mat3x3 == tp_mat3x3);
assert(color32 == tp_color32);
/* vec2 */ /* vec2 */
py_bindmagic(vec2, __new__, vec2__new__); py_bindmagic(vec2, __new__, vec2__new__);
@ -997,6 +1241,31 @@ void pk__add_module_linalg() {
(c11_vec3){ (c11_vec3){
{1, 1, 1} {1, 1, 1}
}); });
/* color32 */
py_bindmagic(color32, __new__, color32__new__);
py_bindmagic(color32, __repr__, color32__repr__);
py_bindmagic(color32, __eq__, color32__eq__);
py_bindmagic(color32, __ne__, color32__ne__);
py_bindproperty(color32, "r", color32__r, NULL);
py_bindproperty(color32, "g", color32__g, NULL);
py_bindproperty(color32, "b", color32__b, NULL);
py_bindproperty(color32, "a", color32__a, NULL);
py_bindmethod(color32, "with_r", color32_with_r);
py_bindmethod(color32, "with_g", color32_with_g);
py_bindmethod(color32, "with_b", color32_with_b);
py_bindmethod(color32, "with_a", color32_with_a);
py_bindstaticmethod(color32, "from_hex", color32_from_hex_STATIC);
py_bindstaticmethod(color32, "from_vec3", color32_from_vec3_STATIC);
py_bindstaticmethod(color32, "from_vec3i", color32_from_vec3i_STATIC);
py_bindmethod(color32, "to_hex", color32_to_hex);
py_bindmethod(color32, "to_vec3", color32_to_vec3);
py_bindmethod(color32, "to_vec3i", color32_to_vec3i);
py_bindmethod(color32, "ansi_fg", color32_ansi_fg);
py_bindmethod(color32, "ansi_bg", color32_ansi_bg);
py_bindfunc(mod, "rgb", linalg_rgb);
py_bindfunc(mod, "rgba", linalg_rgba);
py_bindstaticmethod(color32, "alpha_blend", color32_alpha_blend_STATIC);
} }
#undef DEFINE_VEC_FIELD #undef DEFINE_VEC_FIELD

25
tests/80_color32.py Normal file
View File

@ -0,0 +1,25 @@
from linalg import color32, rgb, rgba
a = color32(100, 200, 255, 120)
assert a.r == 100
assert a.g == 200
assert a.b == 255
assert a.a == 120
assert a.with_r(255).r == 255
assert a.with_g(255).g == 255
assert a.with_b(255).b == 255
assert a.with_a(255).a == 255 and a.with_a(255).g == a.g
assert a.to_hex() == '#64c8ff78'
assert color32.from_hex('#64c8ff78') == a
assert rgb(100, 200, 255) != a
assert rgba(100, 200, 255, 120 / 255) == a
b = color32(75, 150, 200, 200)
assert a == a and b == b
assert a != b
assert repr(b) == 'color32(75, 150, 200, 200)'
# assert color32.alpha_blend(a, b) == color32(86, 173, 225, 162)