fix qr code encoding to always be array buffers instead of strings
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "stb_rect_pack.h"
|
||||
#define STB_DXT_IMPLEMENTATION
|
||||
#include "stb_dxt.h"
|
||||
#include "stb_image_write.h"
|
||||
#include "string.h"
|
||||
#include "spline.h"
|
||||
#include <assert.h>
|
||||
@@ -5597,8 +5598,8 @@ JSC_CCALL(surface_pitch,
|
||||
static const JSCFunctionListEntry js_SDL_Surface_funcs[] = {
|
||||
MIST_FUNC_DEF(surface, blit, 3),
|
||||
MIST_FUNC_DEF(surface, scale, 1),
|
||||
MIST_FUNC_DEF(surface,fill,1),
|
||||
MIST_FUNC_DEF(surface,rect,2),
|
||||
MIST_FUNC_DEF(surface, fill,1),
|
||||
MIST_FUNC_DEF(surface, rect,2),
|
||||
MIST_FUNC_DEF(surface, dup, 0),
|
||||
MIST_FUNC_DEF(surface, pixels, 0),
|
||||
MIST_FUNC_DEF(surface, convert, 1),
|
||||
@@ -7298,6 +7299,19 @@ JSC_CCALL(graphics_hsl_to_rgb,
|
||||
return color2js(js, (colorf){r+m, g+m, b+m, 1});
|
||||
)
|
||||
|
||||
JSC_CCALL(graphics_save_png,
|
||||
const char *file = JS_ToCString(js, argv[0]);
|
||||
int w, h, comp, pitch;
|
||||
JS_ToInt32(js, &w, argv[1]);
|
||||
JS_ToInt32(js, &h, argv[2]);
|
||||
JS_ToInt32(js, &pitch, argv[4]);
|
||||
size_t size;
|
||||
void *data = JS_GetArrayBuffer(js, &size, argv[3]);
|
||||
|
||||
if (!stbi_write_png(file, w, h, 4, data, pitch))
|
||||
return JS_ThrowInternalError(js, "Could not write png");
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_graphics_funcs[] = {
|
||||
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
|
||||
MIST_FUNC_DEF(gpu, make_sprite_queue, 4),
|
||||
@@ -7314,6 +7328,7 @@ static const JSCFunctionListEntry js_graphics_funcs[] = {
|
||||
MIST_FUNC_DEF(os, make_sprite, 1),
|
||||
MIST_FUNC_DEF(os, make_line_prim, 5),
|
||||
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
||||
MIST_FUNC_DEF(graphics, save_png, 4),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_video_funcs[] = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <math.h> // for sqrt
|
||||
#include <stdlib.h> // for size_t, etc.
|
||||
#include <string.h> // for memcpy, strcmp
|
||||
#include <stdint.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
@@ -18,11 +19,28 @@ static JSValue js_qr_encode(JSContext *js, JSValueConst this_val, int argc, JSVa
|
||||
int version = 0; // 0 means auto-select in qrencode
|
||||
int errc = QR_ECLEVEL_L; // Default to "L"
|
||||
int casesensitive = 1; // Default to true (qrencode treats 1 as case-sensitive)
|
||||
int use_byte_mode = 0;
|
||||
|
||||
// Handle options object (argv[1])
|
||||
if (argc > 1 && !JS_IsUndefined(argv[1]) && JS_IsObject(argv[1])) {
|
||||
JSValue opt = argv[1];
|
||||
|
||||
JSValue m = JS_GetPropertyStr(js, opt, "mode");
|
||||
if (!JS_IsUndefined(m)) {
|
||||
const char *smode = JS_ToCString(js, m);
|
||||
if (!smode) { JS_FreeValue(js, m); return JS_EXCEPTION; }
|
||||
if (!strcasecmp(smode, "byte") || !strcasecmp(smode, "binary"))
|
||||
use_byte_mode = 1;
|
||||
else if (!strcasecmp(smode, "text"))
|
||||
use_byte_mode = 0;
|
||||
else {
|
||||
JS_FreeCString(js, smode); JS_FreeValue(js, m);
|
||||
return JS_ThrowRangeError(js, "mode must be 'byte' or 'text'");
|
||||
}
|
||||
JS_FreeCString(js, smode);
|
||||
JS_FreeValue(js, m);
|
||||
}
|
||||
|
||||
// Version (1-40)
|
||||
JSValue v = JS_GetPropertyStr(js, opt, "version");
|
||||
if (!JS_IsUndefined(v)) {
|
||||
@@ -75,14 +93,14 @@ static JSValue js_qr_encode(JSContext *js, JSValueConst this_val, int argc, JSVa
|
||||
int must_free_data = 0;
|
||||
|
||||
// Handle string input
|
||||
if (JS_IsString(argv[0])) {
|
||||
if (!use_byte_mode && JS_IsString(argv[0])) {
|
||||
data = JS_ToCStringLen(js, &data_len, argv[0]);
|
||||
if (!data) {
|
||||
return JS_ThrowTypeError(js, "Invalid string input");
|
||||
}
|
||||
must_free_data = 1;
|
||||
} else {
|
||||
// Check if it's an ArrayBuffer
|
||||
/* treat everything else as raw bytes */
|
||||
uint8_t *buf = JS_GetArrayBuffer(js, &data_len, argv[0]);
|
||||
if (!buf) {
|
||||
return JS_ThrowTypeError(js, "encode expects a string or ArrayBuffer");
|
||||
@@ -91,7 +109,12 @@ static JSValue js_qr_encode(JSContext *js, JSValueConst this_val, int argc, JSVa
|
||||
}
|
||||
|
||||
// Encode using qrencode
|
||||
QRcode *qrcode = QRcode_encodeString(data, version, errc, QR_MODE_8, casesensitive);
|
||||
QRcode *qrcode;
|
||||
if (use_byte_mode)
|
||||
qrcode = QRcode_encodeData(data_len, data, version, errc);
|
||||
else
|
||||
qrcode = QRcode_encodeString(data, version, errc, QR_MODE_8, casesensitive);
|
||||
|
||||
if (!qrcode) {
|
||||
if (must_free_data)
|
||||
JS_FreeCString(js, data);
|
||||
@@ -189,7 +212,8 @@ static JSValue js_qr_decode(JSContext *js, JSValueConst this_val, int argc, JSVa
|
||||
quirc_extract(qr, i, &code);
|
||||
printf("code is %p\n", code.size);
|
||||
if (quirc_decode(&code, &qdata) == QUIRC_SUCCESS) {
|
||||
JSValue item = JS_NewStringLen(js, (const char *)qdata.payload, qdata.payload_len);
|
||||
JSValue item = JS_NewArrayBufferCopy(js, qdata.payload, qdata.payload_len);
|
||||
// JSValue item = JS_NewStringLen(js, (const char *)qdata.payload, qdata.payload_len);
|
||||
JS_SetPropertyUint32(js, result, i, item);
|
||||
}
|
||||
}
|
||||
@@ -198,10 +222,71 @@ static JSValue js_qr_decode(JSContext *js, JSValueConst this_val, int argc, JSVa
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_qr_rgba(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js, "qr.rgba(mods, ...) needs a modules object");
|
||||
|
||||
JSValue mods = argv[0];
|
||||
|
||||
uint32_t on = 0xFF000000u; /* black opaque */
|
||||
uint32_t off = 0xFFFFFFFFu; /* white opaque */
|
||||
if (argc > 1) JS_ToUint32(js, &on, argv[1]);
|
||||
if (argc > 2) JS_ToUint32(js, &off, argv[2]);
|
||||
|
||||
JSValue js_w = JS_GetPropertyStr(js, mods, "width");
|
||||
if (JS_IsUndefined(js_w)) {
|
||||
JS_FreeValue(js, js_w);
|
||||
return JS_ThrowTypeError(js, "mods.width missing");
|
||||
}
|
||||
int32_t m = 0;
|
||||
JS_ToInt32(js, &m, js_w);
|
||||
JS_FreeValue(js, js_w);
|
||||
|
||||
if (m <= 0)
|
||||
return JS_ThrowRangeError(js, "mods.width must be > 0");
|
||||
|
||||
JSValue js_bits = JS_GetPropertyStr(js, mods, "data");
|
||||
size_t len = JS_ArrayLength(js, js_bits);
|
||||
|
||||
if (len < (size_t)m * (size_t)m) {
|
||||
JS_FreeValue(js, js_bits);
|
||||
return JS_ThrowRangeError(js, "mods.data too small for width²");
|
||||
}
|
||||
|
||||
const int w = m, h = m, pitch = w * 4;
|
||||
const size_t bytes = (size_t)h * (size_t)pitch;
|
||||
uint8_t *tmp = js_malloc(js, bytes);
|
||||
if (!tmp) { JS_FreeValue(js, js_bits); return JS_EXCEPTION; }
|
||||
|
||||
for (int y = 0; y < h; ++y) {
|
||||
uint32_t *row = (uint32_t *)(tmp + y * pitch);
|
||||
for (int x = 0; x < w; x++) {
|
||||
uint32_t idx = y * m + x;
|
||||
JSValue vbit = JS_GetPropertyUint32(js, js_bits, idx);
|
||||
int on_bit = JS_ToBool(js, vbit);
|
||||
JS_FreeValue(js, vbit);
|
||||
row[x] = on_bit ? on : off;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue ab = JS_NewArrayBufferCopy(js, tmp, bytes); /* GC owns copy */
|
||||
js_free(js, tmp);
|
||||
JS_FreeValue(js, js_bits); /* done with src */
|
||||
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, w));
|
||||
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, h));
|
||||
JS_SetPropertyStr(js, obj, "pitch", JS_NewInt32(js, pitch));
|
||||
JS_SetPropertyStr(js, obj, "buffer", ab);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Exported functions for the module
|
||||
static const JSCFunctionListEntry js_qr_funcs[] = {
|
||||
JS_CFUNC_DEF("encode", 2, js_qr_encode), // Updated to expect 2 args
|
||||
JS_CFUNC_DEF("decode", 4, js_qr_decode),
|
||||
JS_CFUNC_DEF("rgba", 3, js_qr_rgba),
|
||||
};
|
||||
|
||||
// Helper to return the default export object
|
||||
|
||||
2056
tests/level.json
Normal file
2056
tests/level.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,5 @@ var myqr = qr.encode("HELLO WORLD", {
|
||||
casesensitive: false
|
||||
})
|
||||
|
||||
|
||||
|
||||
console.log("test finished success.")
|
||||
os.exit()
|
||||
|
||||
@@ -39,11 +39,35 @@ io.mount('/')
|
||||
|
||||
var qr = use('qr')
|
||||
|
||||
var myimg = qr.encode("HELLO WORLD")
|
||||
var mybytes = qr.rgba(myimg)
|
||||
|
||||
graphics.save_png("qr_hello.png", mybytes.width, mybytes.height, mybytes.buffer, mybytes.pitch);
|
||||
|
||||
// test saving a zipped json
|
||||
var miniz = use('miniz')
|
||||
|
||||
var lvl = io.slurp('tests/level.json')
|
||||
var lvl_cmp = miniz.compress(lvl)
|
||||
|
||||
var qr_lvl = qr.encode(lvl_cmp)
|
||||
var lvl_bytes = qr.rgba(qr_lvl)
|
||||
graphics.save_png("qr_level.png", lvl_bytes.width, lvl_bytes.height, lvl_bytes.buffer, lvl_bytes.pitch)
|
||||
|
||||
console.log(`saved compressed level as qr; size was ${lvl_cmp.byteLength}`)
|
||||
|
||||
$_.send(ioguy, {
|
||||
type: "subscribe",
|
||||
actor: $_
|
||||
})
|
||||
|
||||
function strToArrayBuffer(binStr) {
|
||||
const view = new Uint8Array(binStr.length);
|
||||
for (let i = 0; i < binStr.length; i++)
|
||||
view[i] = binStr.codePointAt(i) & 0xff; // mask keeps it 0-255
|
||||
return view.buffer;
|
||||
}
|
||||
|
||||
$_.receiver(e => {
|
||||
if (e.type === 'quit')
|
||||
os.exit()
|
||||
@@ -53,7 +77,12 @@ $_.receiver(e => {
|
||||
console.log(`got ${e.data} dropped`)
|
||||
img = e.data
|
||||
var image = graphics.texture(e.data)
|
||||
console.log(json.encode(qr.decode(image.surface.pixels(), image.surface.width(), image.surface.height(), image.surface.pitch())))
|
||||
var data = qr.decode(image.surface.pixels(), image.surface.width(), image.surface.height(), image.surface.pitch())
|
||||
console.log(`found ${data.length} qr`)
|
||||
data = data[0]
|
||||
console.log(data.byteLength)
|
||||
var ddata = miniz.decompress(data, true)
|
||||
console.log(ddata)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user