221 lines
5.0 KiB
C++
221 lines
5.0 KiB
C++
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#define EXTENSION_NAME QRCode
|
|
#define LIB_NAME "QRCode"
|
|
#define MODULE_NAME "qrcode"
|
|
|
|
// Defold SDK
|
|
#define DLIB_LOG_DOMAIN LIB_NAME
|
|
#include <dmsdk/sdk.h>
|
|
|
|
#include "quirc/quirc.h"
|
|
|
|
#define JC_QRENCODE_IMPLEMENTATION
|
|
#include "jc_qrencode.h"
|
|
|
|
// GENERATE
|
|
// https://github.com/nayuki/QR-Code-generator
|
|
|
|
struct QRCodeContext
|
|
{
|
|
int width;
|
|
int height;
|
|
};
|
|
|
|
QRCodeContext g_QRContext;
|
|
|
|
static int Scan(lua_State* L)
|
|
{
|
|
DM_LUA_STACK_CHECK(L, 1);
|
|
|
|
dmScript::LuaHBuffer* buffer = dmScript::CheckBuffer(L, 1);
|
|
int width = luaL_checkint(L, 2);
|
|
int height = luaL_checkint(L, 3);
|
|
int flip_x = luaL_checkint(L, 4);
|
|
|
|
struct quirc* qr;
|
|
|
|
qr = quirc_new();
|
|
if (!qr) {
|
|
return luaL_error(L, "qrcode.scan: Failed to allocate memory");
|
|
}
|
|
|
|
if (quirc_resize(qr, width, height) < 0) {
|
|
return luaL_error(L, "qrcode.scan: Failed to allocate video memory");
|
|
}
|
|
|
|
uint8_t* image = quirc_begin(qr, &width, &height);
|
|
|
|
uint8_t* data;
|
|
uint32_t datasize;
|
|
dmBuffer::GetBytes(buffer->m_Buffer, (void**)&data, &datasize);
|
|
|
|
// Make it grey scale
|
|
for( int y = 0; y < height; ++y )
|
|
{
|
|
for( int x = 0; x < width; ++x )
|
|
{
|
|
float r = (float)data[y * width * 3 + x * 3 + 0] / 255.0f;
|
|
float g = (float)data[y * width * 3 + x * 3 + 1] / 255.0f;
|
|
float b = (float)data[y * width * 3 + x * 3 + 2] / 255.0f;
|
|
|
|
float value = (r+g+b)/3.0f;
|
|
|
|
value = value*value;
|
|
value += 0.1f;
|
|
if(value>1.0f)
|
|
{
|
|
value = 1.0f;
|
|
}
|
|
|
|
if( flip_x == 0 )
|
|
image[y * width + x] = (uint8_t)(value * 255.0f);
|
|
else
|
|
image[y * width + (width - x - 1)] = (uint8_t)(value * 255.0f);
|
|
}
|
|
}
|
|
|
|
quirc_end(qr);
|
|
|
|
int num_codes = quirc_count(qr);
|
|
for( int i = 0; i < num_codes; i++)
|
|
{
|
|
struct quirc_code code;
|
|
struct quirc_data data;
|
|
|
|
quirc_extract(qr, i, &code);
|
|
|
|
quirc_decode_error_t err = quirc_decode(&code, &data);
|
|
if (err)
|
|
{
|
|
num_codes = 0;
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, (const char*)data.payload);
|
|
}
|
|
break;
|
|
}
|
|
|
|
quirc_destroy(qr);
|
|
|
|
if( num_codes == 0 )
|
|
{
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static dmBuffer::HBuffer GenerateImage(JCQRCode* qr, uint32_t* outsize)
|
|
{
|
|
int32_t size = qr->size;
|
|
int32_t border = 2;
|
|
int32_t scale = 8;
|
|
int32_t newsize = scale*(size + 2 * border);
|
|
|
|
dmBuffer::StreamDeclaration streams_decl[] = {
|
|
{dmHashString64("data"), dmBuffer::VALUE_TYPE_UINT8, 1}
|
|
};
|
|
|
|
dmBuffer::HBuffer buffer = 0;
|
|
dmBuffer::Result result = dmBuffer::Create(newsize * newsize, streams_decl, 1, &buffer);
|
|
if (result != dmBuffer::RESULT_OK )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint8_t* data;
|
|
uint32_t datasize;
|
|
dmBuffer::GetBytes(buffer, (void**)&data, &datasize);
|
|
|
|
memset(data, 255, newsize*newsize);
|
|
|
|
for( int y = 0; y < size*scale; ++y )
|
|
{
|
|
for( int x = 0; x < size*scale; ++x )
|
|
{
|
|
int flip_y = size*scale - y - 1; // flip it so we can easily pass it as a texture later on
|
|
uint8_t module = qr->data[(flip_y/scale)*256 + (x/scale)];
|
|
data[(y + scale*border) * newsize + x + scale*border] = module;
|
|
}
|
|
}
|
|
|
|
if (outsize)
|
|
*outsize = newsize;
|
|
return buffer;
|
|
}
|
|
|
|
static int Generate(lua_State* L)
|
|
{
|
|
DM_LUA_STACK_CHECK(L, 2);
|
|
|
|
const char* text = luaL_checkstring(L, 1);
|
|
|
|
JCQRCode* qr = jc_qrencode((const uint8_t*)text, (uint32_t)strlen(text));
|
|
if( !qr )
|
|
{
|
|
return DM_LUA_ERROR("Failed to encode text: '%s'\n", text);
|
|
}
|
|
|
|
uint32_t outsize = 0;
|
|
dmBuffer::HBuffer buffer = GenerateImage(qr, &outsize);
|
|
|
|
free(qr); // free the qr code
|
|
|
|
// Transfer ownership to Lua
|
|
dmScript::LuaHBuffer luabuffer = {buffer, true};
|
|
dmScript::PushBuffer(L, luabuffer);
|
|
lua_pushinteger(L, outsize);
|
|
return 2;
|
|
}
|
|
|
|
static const luaL_reg Module_methods[] =
|
|
{
|
|
{"scan", Scan},
|
|
{"generate", Generate},
|
|
{0, 0}
|
|
};
|
|
|
|
static void LuaInit(lua_State* L)
|
|
{
|
|
int top = lua_gettop(L);
|
|
luaL_register(L, MODULE_NAME, Module_methods);
|
|
|
|
#define SETCONSTANT(name) \
|
|
lua_pushnumber(L, (lua_Number) name); \
|
|
lua_setfield(L, -2, #name);\
|
|
|
|
#undef SETCONSTANT
|
|
|
|
lua_pop(L, 1);
|
|
assert(top == lua_gettop(L));
|
|
}
|
|
|
|
dmExtension::Result AppInitializeQRCode(dmExtension::AppParams* params)
|
|
{
|
|
return dmExtension::RESULT_OK;
|
|
}
|
|
|
|
dmExtension::Result InitializeQRCode(dmExtension::Params* params)
|
|
{
|
|
LuaInit(params->m_L);
|
|
return dmExtension::RESULT_OK;
|
|
}
|
|
|
|
dmExtension::Result AppFinalizeQRCode(dmExtension::AppParams* params)
|
|
{
|
|
return dmExtension::RESULT_OK;
|
|
}
|
|
|
|
dmExtension::Result FinalizeQRCode(dmExtension::Params* params)
|
|
{
|
|
return dmExtension::RESULT_OK;
|
|
}
|
|
|
|
|
|
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, AppInitializeQRCode, AppFinalizeQRCode, InitializeQRCode, 0, 0, FinalizeQRCode)
|