initial add
This commit is contained in:
133
dxt.c
Normal file
133
dxt.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#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)
|
||||
Reference in New Issue
Block a user