Compare commits

...

5 Commits

Author SHA1 Message Date
blueloveTH
3c36b30f04 Create cute_png.md 2025-08-06 14:48:50 +08:00
blueloveTH
adef3f523c Update CMakeLists.txt 2025-08-06 14:42:29 +08:00
blueloveTH
f7061ce6d3 Update CMakeLists.txt 2025-08-06 14:37:39 +08:00
blueloveTH
84d7292b5d add color32.__hash__ 2025-08-06 14:33:22 +08:00
blueloveTH
391e83d659 add cute_png module from cute_headers 2025-08-06 14:25:53 +08:00
20 changed files with 2031 additions and 27 deletions

View File

@ -36,7 +36,7 @@ jobs:
shell: bash
run: |
mkdir -p output/x86_64
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_CUTE_PNG=ON
cp main.exe output/x86_64
cp pocketpy.dll output/x86_64
- uses: actions/upload-artifact@v4
@ -76,7 +76,7 @@ jobs:
run: |
python scripts/check_pragma_once.py include
mkdir -p output/x86_64
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_CUTE_PNG=ON
python scripts/run_tests.py
cp main output/x86_64
cp libpocketpy.so output/x86_64
@ -96,7 +96,7 @@ jobs:
submodules: recursive
- name: Compile and Test
run: |
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_CUTE_PNG=ON
python scripts/run_tests.py
- name: Benchmark
run: python scripts/run_tests.py benchmark

View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.10)
project(cute_png)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME} STATIC src/cute_png.c)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/include
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
#include "pocketpy.h"
#define CUTE_PNG_IMPLEMENTATION
#include "cute_png.h"
static bool cute_png_loads(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);
py_newarray2d(py_retval(), image.w, image.h);
for(int y = 0; y < image.h; y++) {
for(int x = 0; x < image.w; x++) {
cp_pixel_t pixel = image.pix[y * image.w + x];
py_ObjectRef slot = py_array2d_getitem(py_retval(), x, y);
c11_color32 color;
color.r = pixel.r;
color.g = pixel.g;
color.b = pixel.b;
color.a = pixel.a;
py_newcolor32(slot, color);
}
}
return true;
}
static bool cute_png_dumps(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_array2d);
int width = py_array2d_getwidth(argv);
int height = py_array2d_getheight(argv);
cp_image_t image = cp_load_blank(width, height);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
py_ObjectRef slot = py_array2d_getitem(argv, x, y);
if(!py_checktype(slot, tp_color32)) return false;
c11_color32 color = py_tocolor32(slot);
cp_pixel_t pixel = cp_make_pixel_a(color.r, color.g, color.b, color.a);
image.pix[y * width + x] = pixel;
}
}
cp_saved_png_t saved_image = cp_save_png_to_memory(&image);
assert(saved_image.data != NULL);
unsigned char* data = py_newbytes(py_retval(), saved_image.size);
memcpy(data, saved_image.data, saved_image.size);
return true;
}
void pk__add_module_cute_png() {
py_GlobalRef mod = py_newmodule("cute_png");
py_bindfunc(mod, "loads", cute_png_loads);
py_bindfunc(mod, "dumps", cute_png_dumps);
}

View File

@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.10)
project(libhv_bindings)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)

View File

@ -5,12 +5,12 @@ project(lz4)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(lz4 STATIC lz4/lib/lz4.c)
add_library(${PROJECT_NAME} STATIC lz4/lib/lz4.c)
target_include_directories(lz4 PRIVATE
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/lz4/lib
)
target_include_directories(lz4 INTERFACE
target_include_directories(${PROJECT_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)

View File

@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.10)
project(pocketpy)
if(PK_BUILD_SHARED_LIB OR NOT PK_BUILD_STATIC_MAIN)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
@ -110,6 +114,11 @@ if(PK_BUILD_MODULE_LIBHV)
add_definitions(-DPK_BUILD_MODULE_LIBHV)
endif()
if(PK_BUILD_MODULE_CUTE_PNG)
add_subdirectory(3rd/cute_png)
add_definitions(-DPK_BUILD_MODULE_CUTE_PNG)
endif()
if(PK_BUILD_SHARED_LIB)
message(">> Building shared library")
@ -175,6 +184,11 @@ if(PK_BUILD_MODULE_LIBHV)
target_link_libraries(${PROJECT_NAME} libhv_bindings)
endif()
if(PK_BUILD_MODULE_CUTE_PNG)
target_link_libraries(${PROJECT_NAME} cute_png)
endif()
if(PK_ENABLE_MIMALLOC)
target_link_libraries(${PROJECT_NAME} mimalloc-static)
endif()

View File

@ -16,7 +16,7 @@ option(PK_ENABLE_MIMALLOC "" OFF)
# modules
option(PK_BUILD_MODULE_LZ4 "" OFF)
option(PK_BUILD_MODULE_LIBHV "" OFF)
option(PK_BUILD_MODULE_CUTE_PNG "" OFF)
# PK_IS_MAIN determines whether the project is being used from root
# or if it is added as a dependency (through add_subdirectory for example).

View File

@ -16,6 +16,7 @@ cmake \
-DPK_BUILD_SHARED_LIB=ON \
-DCMAKE_BUILD_TYPE=Release \
-DPK_BUILD_MODULE_LZ4=ON \
-DPK_BUILD_MODULE_LIBHV=ON
-DPK_BUILD_MODULE_LIBHV=ON \
-DPK_BUILD_CUTE_PNG=ON
cmake --build . --config Release

View File

@ -4,4 +4,5 @@
-std=c11
-Iinclude/
-I3rd/lz4/
-I3rd/libhv/include/
-I3rd/libhv/include/
-I3rd/cute_headers/include/

14
docs/modules/cute_png.md Normal file
View File

@ -0,0 +1,14 @@
---
icon: package
label: cute_png
---
!!!
This module is optional. Set option `PK_BUILD_MODULE_CUTE_PNG` to `ON` in your `CMakeLists.txt` to enable it.
!!!
Wraps [cute_png.h](https://github.com/RandyGaul/cute_headers/blob/master/cute_png.h) for PNG image loading and saving.
#### Source code
:::code source="../../include/typings/cute_png.pyi" :::

View File

@ -31,7 +31,7 @@ typedef struct c11_array2d_view {
c11_vec2i origin;
} c11_array2d_view;
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows);
c11_array2d* c11_newarray2d(py_OutRef out, int n_cols, int n_rows);
/* chunked_array2d */
#define SMALLMAP_T__HEADER

View File

@ -31,4 +31,10 @@ void pk__add_module_pkpy();
void pk__add_module_libhv();
#else
#define pk__add_module_libhv()
#endif
#endif
#ifdef PK_BUILD_MODULE_CUTE_PNG
void pk__add_module_cute_png();
#else
#define pk__add_module_cute_png()
#endif

View File

@ -39,8 +39,6 @@ typedef struct py_TValue {
char _chars[16];
};
} py_TValue;
static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24");
#endif
/// A string view type. It is helpful for passing strings which are not null-terminated.
@ -754,6 +752,13 @@ PK_API py_f64 py_Random_random(py_Ref self);
PK_API py_f64 py_Random_uniform(py_Ref self, py_f64 a, py_f64 b);
PK_API py_i64 py_Random_randint(py_Ref self, py_i64 a, py_i64 b);
/************* array2d module *************/
PK_API void py_newarray2d(py_OutRef out, int width, int height);
PK_API int py_array2d_getwidth(py_Ref self);
PK_API int py_array2d_getheight(py_Ref self);
PK_API py_ObjectRef py_array2d_getitem(py_Ref self, int x, int y);
PK_API void py_array2d_setitem(py_Ref self, int x, int y, py_Ref val);
/************* vmath module *************/
PK_API void py_newvec2(py_OutRef out, c11_vec2);
PK_API void py_newvec3(py_OutRef out, c11_vec3);

View File

@ -0,0 +1,5 @@
from array2d import array2d
from vmath import color32
def loads(data: bytes) -> array2d[color32]: ...
def dumps(image: array2d[color32]) -> bytes: ...

View File

@ -186,6 +186,7 @@ class color32:
def __eq__(self, other: object) -> bool: ...
def __ne__(self, other: object) -> bool: ...
def __repr__(self) -> str: ...
def __hash__(self) -> int: ...
@property
def r(self) -> int: ...

View File

@ -236,6 +236,7 @@ void VM__ctor(VM* self) {
pk__add_module_conio();
pk__add_module_lz4(); // optional
pk__add_module_libhv(); // optional
pk__add_module_cute_png(); // optional
pk__add_module_pkpy();
// add python builtins

View File

@ -16,7 +16,7 @@ static bool c11_array2d__set(c11_array2d* self, int col, int row, py_Ref value)
return true;
}
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows) {
c11_array2d* c11_newarray2d(py_OutRef out, int n_cols, int n_rows) {
int numel = n_cols * n_rows;
c11_array2d* ud = py_newobject(out, tp_array2d, numel, sizeof(c11_array2d));
ud->header.n_cols = n_cols;
@ -112,7 +112,10 @@ static bool array2d_like_index(int argc, py_Ref argv) {
int code = py_equal(item, value);
if(code == -1) return false;
if(code == 1) {
py_newvec2i(py_retval(), (c11_vec2i){{i, j}});
py_newvec2i(py_retval(),
(c11_vec2i){
{i, j}
});
return true;
}
}
@ -176,7 +179,7 @@ static bool array2d_like_map(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_array2d_like* self = py_touserdata(argv);
py_Ref f = py_arg(1);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j);
@ -228,7 +231,7 @@ static bool _array2d_like_broadcasted_zip_with(int argc, py_Ref argv, py_Name op
} else {
other = NULL;
}
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref lhs = self->f_get(self, i, j);
@ -254,7 +257,7 @@ static bool array2d_like_zip_with(int argc, py_Ref argv) {
c11_array2d_like* other = py_touserdata(py_arg(1));
py_Ref f = py_arg(2);
if(!_array2d_like_check_same_shape(self, other)) return false;
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_push(f);
@ -299,7 +302,7 @@ DEF_ARRAY2D_LIKE__MAGIC_ZIP_WITH(__xor__, __xor__, 0)
static bool array2d_like__invert__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_array2d_like* self = py_touserdata(argv);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j);
@ -316,7 +319,7 @@ static bool array2d_like_copy(int argc, py_Ref argv) {
// def copy(self) -> 'array2d': ...
PY_CHECK_ARGC(1);
c11_array2d_like* self = py_touserdata(argv);
c11_array2d* res = py_newarray2d(py_retval(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_retval(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j);
@ -637,7 +640,7 @@ static bool array2d_like_get_bounding_rect(int argc, py_Ref argv) {
static bool array2d_like_count_neighbors(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
c11_array2d_like* self = py_touserdata(argv);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
py_Ref value = py_arg(1);
const char* neighborhood = py_tostr(py_arg(2));
@ -703,7 +706,7 @@ static bool array2d_like_convolve(int argc, py_Ref argv) {
int ksize = kernel->n_cols;
if(ksize % 2 == 0) return ValueError("kernel size must be odd");
int ksize_half = ksize / 2;
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_i64 sum = 0;
@ -834,7 +837,7 @@ static bool array2d__new__(int argc, py_Ref argv) {
int n_cols = argv[1]._i64;
int n_rows = argv[2]._i64;
if(n_cols <= 0 || n_rows <= 0) return ValueError("array2d() expected positive dimensions");
c11_array2d* ud = py_newarray2d(py_pushtmp(), n_cols, n_rows);
c11_array2d* ud = c11_newarray2d(py_pushtmp(), n_cols, n_rows);
// setup initial values
if(py_callable(default_)) {
for(int j = 0; j < n_rows; j++) {
@ -876,7 +879,7 @@ static bool array2d_fromlist_STATIC(int argc, py_Ref argv) {
return ValueError("fromlist() expected a list of lists with the same length");
}
}
c11_array2d* res = py_newarray2d(py_retval(), n_cols, n_rows);
c11_array2d* res = c11_newarray2d(py_retval(), n_cols, n_rows);
for(int j = 0; j < n_rows; j++) {
py_Ref row_j = py_list_getitem(argv, j);
for(int i = 0; i < n_cols; i++) {
@ -1359,4 +1362,30 @@ void pk__add_module_array2d() {
register_array2d(mod);
register_array2d_view(mod);
register_chunked_array2d(mod);
}
void py_newarray2d(py_OutRef out, int width, int height) { c11_newarray2d(out, width, height); }
int py_array2d_getwidth(py_Ref self) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
return ud->header.n_cols;
}
int py_array2d_getheight(py_Ref self) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
return ud->header.n_rows;
}
py_ObjectRef py_array2d_getitem(py_Ref self, int x, int y) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
return c11_array2d__get(ud, x, y);
}
void py_array2d_setitem(py_Ref self, int x, int y, py_Ref value) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
c11_array2d__set(ud, x, y, value);
}

View File

@ -656,7 +656,7 @@ bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_
case PKL_ARRAY2D: {
int n_cols = pkl__read_int(&p);
int n_rows = pkl__read_int(&p);
c11_array2d* arr = py_newarray2d(py_pushtmp(), n_cols, n_rows);
c11_array2d* arr = c11_newarray2d(py_pushtmp(), n_cols, n_rows);
int total_size = arr->header.numel * sizeof(py_TValue);
memcpy(arr->data, p, total_size);
for(int i = 0; i < arr->header.numel; i++) {

View File

@ -1011,6 +1011,14 @@ static bool color32__repr__(int argc, py_Ref argv) {
return true;
}
static bool color32__hash__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
uint32_t* color_int = (uint32_t*)&color;
py_newint(py_retval(), *color_int);
return true;
}
static bool color32_ansi_fg(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_color32 color = py_tocolor32(argv);
@ -1256,6 +1264,7 @@ void pk__add_module_vmath() {
py_bindmagic(color32, __repr__, color32__repr__);
py_bindmagic(color32, __eq__, color32__eq__);
py_bindmagic(color32, __ne__, color32__ne__);
py_bindmagic(color32, __hash__, color32__hash__);
py_bindproperty(color32, "r", color32__r, NULL);
py_bindproperty(color32, "g", color32__g, NULL);
py_bindproperty(color32, "b", color32__b, NULL);