#include "cell.h" #include #include #include #define STB_DXT_IMPLEMENTATION #include "stb_dxt.h" // Helper function to map DXT compression types to pixel format strings static const char* get_dxt_format_string(int type) { switch (type) { case 1: return "bc1"; case 3: return "bc2"; case 5: return "bc3"; default: return "bc3"; } } // DXT compression/encoding JSValue js_dxt_encode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) { if (argc < 1) return JS_ThrowTypeError(js, "dxt.encode requires an image object"); JSValue width_val = JS_GetPropertyStr(js, argv[0], "width"); JSValue height_val = JS_GetPropertyStr(js, argv[0], "height"); if (JS_IsNull(width_val) || JS_IsNull(height_val)) { JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); return JS_ThrowTypeError(js, "dxt.encode requires width and height properties"); } int width, height; if (JS_ToInt32(js, &width, width_val) < 0 || JS_ToInt32(js, &height, height_val) < 0) { JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); return JS_ThrowTypeError(js, "width and height must be numbers"); } JS_FreeValue(js, width_val); JS_FreeValue(js, height_val); if (width < 4 || height < 4) return JS_ThrowRangeError(js, "width and height must be at least 4 for DXT compression"); // Dimensions must be multiples of 4 if (width % 4 != 0 || height % 4 != 0) return JS_ThrowRangeError(js, "width and height must be multiples of 4 for DXT compression"); JSValue pixels_val = JS_GetPropertyStr(js, argv[0], "pixels"); size_t pixel_len; void *pixel_data = js_get_blob_data(js, &pixel_len, pixels_val); JS_FreeValue(js, pixels_val); if (pixel_data == NULL) return JS_EXCEPTION; if (!pixel_data) return JS_ThrowTypeError(js, "pixels blob has no data"); size_t required_size = width * height * 4; if (pixel_len < required_size) return JS_ThrowRangeError(js, "pixels buffer too small (need %zu, got %zu)", required_size, pixel_len); // Get compression options int compression_type = 5; // Default DXT5/BC3 int high_quality = 1; if (argc > 1 && JS_IsObject(argv[1])) { JSValue type_val = JS_GetPropertyStr(js, argv[1], "type"); if (!JS_IsNull(type_val)) { JS_ToInt32(js, &compression_type, type_val); } JS_FreeValue(js, type_val); JSValue quality_val = JS_GetPropertyStr(js, argv[1], "high_quality"); if (!JS_IsNull(quality_val)) { high_quality = JS_ToBool(js, quality_val); } JS_FreeValue(js, quality_val); } // Determine alpha mode based on compression type int alpha_mode = (compression_type == 1) ? 0 : 1; // BC1=no alpha block, BC3=alpha block int blocks_x = width / 4; int blocks_y = height / 4; int bytes_per_block = (alpha_mode == 0) ? 8 : 16; size_t output_size = blocks_x * blocks_y * bytes_per_block; void *output = malloc(output_size); if (!output) return JS_ThrowOutOfMemory(js); int mode = high_quality ? STB_DXT_HIGHQUAL : STB_DXT_NORMAL; for (int by = 0; by < blocks_y; by++) { for (int bx = 0; bx < blocks_x; bx++) { unsigned char block[64]; // 4x4 RGBA = 64 bytes // Extract 4x4 block for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int src_x = bx * 4 + x; int src_y = by * 4 + y; int src_idx = (src_y * width + src_x) * 4; int dst_idx = (y * 4 + x) * 4; block[dst_idx + 0] = ((unsigned char*)pixel_data)[src_idx + 0]; block[dst_idx + 1] = ((unsigned char*)pixel_data)[src_idx + 1]; block[dst_idx + 2] = ((unsigned char*)pixel_data)[src_idx + 2]; block[dst_idx + 3] = ((unsigned char*)pixel_data)[src_idx + 3]; } } int output_idx = (by * blocks_x + bx) * bytes_per_block; stb_compress_dxt_block(((unsigned char*)output) + output_idx, block, alpha_mode, mode); } } JSValue result = JS_NewObject(js); JS_SetPropertyStr(js, result, "width", JS_NewInt32(js, width)); JS_SetPropertyStr(js, result, "height", JS_NewInt32(js, height)); JS_SetPropertyStr(js, result, "format", JS_NewString(js, get_dxt_format_string(compression_type))); JS_SetPropertyStr(js, result, "compression", JS_NewInt32(js, compression_type)); JS_SetPropertyStr(js, result, "pixels", js_new_blob_stoned_copy(js, output, output_size)); free(output); return result; } static const JSCFunctionListEntry js_dxt_funcs[] = { MIST_FUNC_DEF(dxt, encode, 2) }; CELL_USE_FUNCS(js_dxt_funcs)