133 lines
4.4 KiB
C
133 lines
4.4 KiB
C
#include "cell.h"
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#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) |