diff --git a/3rd/cute_png/src/cute_png.c b/3rd/cute_png/src/cute_png.c index 57547b46..23191555 100644 --- a/3rd/cute_png/src/cute_png.c +++ b/3rd/cute_png/src/cute_png.c @@ -23,6 +23,7 @@ static bool cute_png_loads(int argc, py_Ref argv) { py_newcolor32(slot, color); } } + cp_free_png(&image); return true; } @@ -45,12 +46,126 @@ static bool cute_png_dumps(int argc, py_Ref argv) { assert(saved_image.data != NULL); unsigned char* data = py_newbytes(py_retval(), saved_image.size); memcpy(data, saved_image.data, saved_image.size); + CUTE_PNG_FREE(saved_image.data); + cp_free_png(&image); + return true; +} + +static void cute_png_Image__dtor(void* ud) { + cp_image_t* image = (cp_image_t*)ud; + cp_free_png(image); +} + +static bool cute_png_Image__new__(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + PY_CHECK_ARG_TYPE(1, tp_int); + PY_CHECK_ARG_TYPE(2, tp_int); + int width = py_toint(py_arg(1)); + int height = py_toint(py_arg(2)); + cp_image_t* ud = py_newobject(py_retval(), + py_totype(argv), + py_gettype("cute_png", py_name("Image")), + sizeof(cp_image_t)); + *ud = cp_load_blank(width, height); + return true; +} + +static bool cute_png_Image__from_bytes_STATIC(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + PY_CHECK_ARG_TYPE(0, tp_bytes); + int size; + unsigned char* data = py_tobytes(argv, &size); + cp_image_t image = cp_load_png_mem(data, size); + if(image.pix == NULL) return ValueError("cute_png: %s", cp_error_reason); + cp_image_t* ud = py_newobject(py_retval(), + py_totype(argv), + py_gettype("cute_png", py_name("Image")), + sizeof(cp_image_t)); + *ud = image; + return true; +} + +static bool cute_png_Image__width(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + cp_image_t* image = py_touserdata(argv); + py_newint(py_retval(), image->w); + return true; +} + +static bool cute_png_Image__height(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + cp_image_t* image = py_touserdata(argv); + py_newint(py_retval(), image->h); + return true; +} + +static bool cute_png_Image__setpixel(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + cp_image_t* image = py_touserdata(argv); + PY_CHECK_ARG_TYPE(1, tp_int); + PY_CHECK_ARG_TYPE(2, tp_int); + PY_CHECK_ARG_TYPE(3, tp_color32); + int x = py_toint(py_arg(1)); + int y = py_toint(py_arg(2)); + c11_color32 color = py_tocolor32(py_arg(3)); + if(x < 0 || x >= image->w || y < 0 || y >= image->h) { + return IndexError("cute_png.Image: index out of range"); + } + cp_pixel_t pixel = cp_make_pixel_a(color.r, color.g, color.b, color.a); + image->pix[y * image->w + x] = pixel; + py_newnone(py_retval()); + return true; +} + +static bool cute_png_Image__getpixel(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + cp_image_t* image = py_touserdata(argv); + PY_CHECK_ARG_TYPE(1, tp_int); + PY_CHECK_ARG_TYPE(2, tp_int); + int x = py_toint(py_arg(1)); + int y = py_toint(py_arg(2)); + if(x < 0 || x >= image->w || y < 0 || y >= image->h) { + return IndexError("cute_png.Image: index out of range"); + } + cp_pixel_t pixel = image->pix[y * image->w + x]; + c11_color32 color; + color.r = pixel.r; + color.g = pixel.g; + color.b = pixel.b; + color.a = pixel.a; + py_newcolor32(py_retval(), color); + return true; +} + +static bool cute_png_Image__clear(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + cp_image_t* image = py_touserdata(argv); + PY_CHECK_ARG_TYPE(1, tp_color32); + c11_color32 color = py_tocolor32(py_arg(1)); + cp_pixel_t pixel = cp_make_pixel_a(color.r, color.g, color.b, color.a); + for(int y = 0; y < image->h; y++) { + for(int x = 0; x < image->w; x++) { + image->pix[y * image->w + x] = pixel; + } + } + py_newnone(py_retval()); return true; } void pk__add_module_cute_png() { py_GlobalRef mod = py_newmodule("cute_png"); + py_Type tp_image = py_newtype("Image", tp_object, mod, cute_png_Image__dtor); py_bindfunc(mod, "loads", cute_png_loads); py_bindfunc(mod, "dumps", cute_png_dumps); + + py_bindmethod(tp_image, "__new__", cute_png_Image__new__); + py_bindstaticmethod(tp_image, "from_bytes", cute_png_Image__from_bytes_STATIC); + + py_bindproperty(tp_image, "width", cute_png_Image__width, NULL); + py_bindproperty(tp_image, "height", cute_png_Image__height, NULL); + + py_bindmethod(tp_image, "setpixel", cute_png_Image__setpixel); + py_bindmethod(tp_image, "getpixel", cute_png_Image__getpixel); + py_bindmethod(tp_image, "clear", cute_png_Image__clear); } \ No newline at end of file diff --git a/include/typings/cute_png.pyi b/include/typings/cute_png.pyi index 1ed40734..f9d3d9a3 100644 --- a/include/typings/cute_png.pyi +++ b/include/typings/cute_png.pyi @@ -1,5 +1,20 @@ from array2d import array2d from vmath import color32 +class Image: + @property + def width(self) -> int: ... + @property + def height(self) -> int: ... + + def __new__(cls, width: int, height: int) -> "Image": ... + + @staticmethod + def from_bytes(data: bytes) -> "Image": ... + + def setpixel(self, x: int, y: int, color: color32) -> None: ... + def getpixel(self, x: int, y: int) -> color32: ... + def clear(self, color: color32) -> None: ... + def loads(data: bytes) -> array2d[color32]: ... def dumps(image: array2d[color32]) -> bytes: ... diff --git a/include/typings/vmath.pyi b/include/typings/vmath.pyi index f2c21fc6..72d7e99e 100644 --- a/include/typings/vmath.pyi +++ b/include/typings/vmath.pyi @@ -213,6 +213,9 @@ class color32: def to_vec3(self) -> vec3: ... def to_vec3i(self) -> vec3i: ... + def to_rgb565(self) -> int: ... + def to_bgr565(self) -> int: ... + def ansi_fg(self, text: str) -> str: ... def ansi_bg(self, text: str) -> str: ... diff --git a/src/modules/vmath.c b/src/modules/vmath.c index fdd6998e..930cd058 100644 --- a/src/modules/vmath.c +++ b/src/modules/vmath.c @@ -976,6 +976,30 @@ static bool color32_to_vec3i(int argc, py_Ref argv) { return true; } +static bool color32_to_rgb565(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_color32 color = py_tocolor32(argv); + c11_color32_premult(&color); + uint16_t r = (color.r >> 3) & 0x1F; + uint16_t g = (color.g >> 2) & 0x3F; + uint16_t b = (color.b >> 3) & 0x1F; + uint16_t rgb565 = (r << 11) | (g << 5) | b; + py_newint(py_retval(), rgb565); + return true; +} + +static bool color32_to_bgr565(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_color32 color = py_tocolor32(argv); + c11_color32_premult(&color); + uint16_t b = (color.b >> 3) & 0x1F; + uint16_t g = (color.g >> 2) & 0x3F; + uint16_t r = (color.r >> 3) & 0x1F; + uint16_t bgr565 = (b << 11) | (g << 5) | r; + py_newint(py_retval(), bgr565); + return true; +} + static bool color32__eq__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); if(argv[1].type != tp_color32) { @@ -1279,6 +1303,8 @@ void pk__add_module_vmath() { 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, "to_rgb565", color32_to_rgb565); + py_bindmethod(color32, "to_bgr565", color32_to_bgr565); py_bindmethod(color32, "ansi_fg", color32_ansi_fg); py_bindmethod(color32, "ansi_bg", color32_ansi_bg); py_bindfunc(mod, "rgb", vmath_rgb);