$
This commit is contained in:
@@ -24,7 +24,7 @@ if (args.length < 2) {
|
|||||||
log.console("Example: cell run convert.ce lenna.png lenna.qoi")
|
log.console("Example: cell run convert.ce lenna.png lenna.qoi")
|
||||||
log.console("Decode: png, jpg, bmp, tga, gif, psd, qoi, ase/aseprite")
|
log.console("Decode: png, jpg, bmp, tga, gif, psd, qoi, ase/aseprite")
|
||||||
log.console("Encode: png, jpg, bmp, tga, qoi")
|
log.console("Encode: png, jpg, bmp, tga, qoi")
|
||||||
$_.stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_file = args[0]
|
var input_file = args[0]
|
||||||
@@ -77,14 +77,14 @@ log.console("Loading input image: " + input_file)
|
|||||||
var input_blob = io.slurp(input_file)
|
var input_blob = io.slurp(input_file)
|
||||||
if (!input_blob) {
|
if (!input_blob) {
|
||||||
log.console("Error: Could not load input file: " + input_file)
|
log.console("Error: Could not load input file: " + input_file)
|
||||||
$_.stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_ext = ext_lower(input_file)
|
var input_ext = ext_lower(input_file)
|
||||||
var decoded = decode_image(input_blob, input_ext)
|
var decoded = decode_image(input_blob, input_ext)
|
||||||
if (!decoded) {
|
if (!decoded) {
|
||||||
log.console("Error: Failed to decode image: " + input_file)
|
log.console("Error: Failed to decode image: " + input_file)
|
||||||
$_.stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console("Image loaded: " + text(decoded.width) + "x" + text(decoded.height) + " pixels")
|
log.console("Image loaded: " + text(decoded.width) + "x" + text(decoded.height) + " pixels")
|
||||||
@@ -92,7 +92,7 @@ log.console("Image loaded: " + text(decoded.width) + "x" + text(decoded.height)
|
|||||||
var output_ext = ext_lower(output_file)
|
var output_ext = ext_lower(output_file)
|
||||||
if (!output_ext) {
|
if (!output_ext) {
|
||||||
log.console("Error: Could not determine output format from: " + output_file)
|
log.console("Error: Could not determine output format from: " + output_file)
|
||||||
$_.stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console("Converting to format: " + output_ext)
|
log.console("Converting to format: " + output_ext)
|
||||||
@@ -100,13 +100,13 @@ log.console("Converting to format: " + output_ext)
|
|||||||
var encoded = encode_image(decoded, output_ext)
|
var encoded = encode_image(decoded, output_ext)
|
||||||
if (!encoded) {
|
if (!encoded) {
|
||||||
log.console("Error: Failed to encode image to format: " + output_ext)
|
log.console("Error: Failed to encode image to format: " + output_ext)
|
||||||
$_.stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
var success = io.slurpwrite(output_file, encoded.pixels)
|
var success = io.slurpwrite(output_file, encoded.pixels)
|
||||||
if (!success) {
|
if (!success) {
|
||||||
log.console("Error: Could not save output file: " + output_file)
|
log.console("Error: Could not save output file: " + output_file)
|
||||||
$_.stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console("Successfully converted " + input_file + " to " + output_file)
|
log.console("Successfully converted " + input_file + " to " + output_file)
|
||||||
|
|||||||
55
gif.c
55
gif.c
@@ -3,7 +3,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "stb_image_common.h"
|
#include "stb_image_common.h"
|
||||||
|
|
||||||
// GIF decoding via stb_image (returns first frame only for animated GIFs)
|
// GIF decoding via stb_image - handles animated GIFs
|
||||||
|
// Returns an object with:
|
||||||
|
// .width, .height - dimensions
|
||||||
|
// .frames - array of frame objects, each with:
|
||||||
|
// .width, .height, .format, .pitch, .pixels, .depth, .hdr
|
||||||
|
// .duration - frame duration in milliseconds (for animated GIFs)
|
||||||
|
// .frame_count - number of frames
|
||||||
JSValue js_gif_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv)
|
JSValue js_gif_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *argv)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -11,31 +17,54 @@ JSValue js_gif_decode(JSContext *js, JSValue this_val, int argc, JSValueConst *a
|
|||||||
if (raw == NULL) return JS_EXCEPTION;
|
if (raw == NULL) return JS_EXCEPTION;
|
||||||
if (!raw) return JS_ThrowReferenceError(js, "could not get GIF data from blob");
|
if (!raw) return JS_ThrowReferenceError(js, "could not get GIF data from blob");
|
||||||
|
|
||||||
int n, width, height;
|
int n, width, height, frames_count;
|
||||||
void *data = stbi_load_from_memory(raw, len, &width, &height, &n, 4);
|
int *delays = NULL;
|
||||||
|
void *data = stbi_load_gif_from_memory(raw, len, &delays, &width, &height, &frames_count, &n, 4);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return JS_ThrowReferenceError(js, "failed to decode GIF: %s", stbi_failure_reason());
|
return JS_ThrowReferenceError(js, "failed to decode GIF: %s", stbi_failure_reason());
|
||||||
|
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
|
if (delays) stbi_image_free(delays);
|
||||||
return JS_ThrowReferenceError(js, "decoded GIF has invalid size: %dx%d", width, height);
|
return JS_ThrowReferenceError(js, "decoded GIF has invalid size: %dx%d", width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pitch = width * 4;
|
int pitch = width * 4;
|
||||||
size_t pixels_size = pitch * height;
|
size_t frame_size = pitch * height;
|
||||||
|
|
||||||
JSValue obj = JS_NewObject(js);
|
// Create frames array
|
||||||
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, width));
|
JSValue frames_array = JS_NewArray(js);
|
||||||
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, height));
|
|
||||||
JS_SetPropertyStr(js, obj, "format", JS_NewString(js, "rgba32"));
|
for (int i = 0; i < frames_count; i++) {
|
||||||
JS_SetPropertyStr(js, obj, "pitch", JS_NewInt32(js, pitch));
|
JSValue frame = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js, obj, "pixels", js_new_blob_stoned_copy(js, data, pixels_size));
|
JS_SetPropertyStr(js, frame, "width", JS_NewInt32(js, width));
|
||||||
JS_SetPropertyStr(js, obj, "depth", JS_NewInt32(js, 8));
|
JS_SetPropertyStr(js, frame, "height", JS_NewInt32(js, height));
|
||||||
JS_SetPropertyStr(js, obj, "hdr", JS_NewBool(js, 0));
|
JS_SetPropertyStr(js, frame, "format", JS_NewString(js, "rgba32"));
|
||||||
|
JS_SetPropertyStr(js, frame, "pitch", JS_NewInt32(js, pitch));
|
||||||
|
|
||||||
|
void *frame_pixels = (unsigned char*)data + (frame_size * i);
|
||||||
|
JS_SetPropertyStr(js, frame, "pixels", js_new_blob_stoned_copy(js, frame_pixels, frame_size));
|
||||||
|
JS_SetPropertyStr(js, frame, "depth", JS_NewInt32(js, 8));
|
||||||
|
JS_SetPropertyStr(js, frame, "hdr", JS_NewBool(js, 0));
|
||||||
|
|
||||||
|
// Add duration in milliseconds (delays are in centiseconds, i.e. 1/100th of a second)
|
||||||
|
int duration_ms = delays ? delays[i] * 10 : 100;
|
||||||
|
JS_SetPropertyStr(js, frame, "duration", JS_NewInt32(js, duration_ms));
|
||||||
|
|
||||||
|
JS_SetPropertyUint32(js, frames_array, i, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create result object
|
||||||
|
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, "frames", frames_array);
|
||||||
|
JS_SetPropertyStr(js, result, "frame_count", JS_NewInt32(js, frames_count));
|
||||||
|
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
return obj;
|
if (delays) stbi_image_free(delays);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_gif_funcs[] = {
|
static const JSCFunctionListEntry js_gif_funcs[] = {
|
||||||
|
|||||||
Reference in New Issue
Block a user