init
This commit is contained in:
258
main/domain/src/raster_tools_image_qr.c
Normal file
258
main/domain/src/raster_tools_image_qr.c
Normal file
@@ -0,0 +1,258 @@
|
||||
#include "raster_tools.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qrcodegen.h"
|
||||
|
||||
#define RASTER_WIDTH 384
|
||||
#define RASTER_BYTES_PER_ROW (RASTER_WIDTH / 8)
|
||||
|
||||
static void set_black(uint8_t *buf, uint16_t width, uint16_t x, uint16_t y) {
|
||||
if (x >= width) {
|
||||
return;
|
||||
}
|
||||
uint16_t bpr = width / 8;
|
||||
size_t idx = (size_t)y * bpr + (x / 8);
|
||||
uint8_t bit = (uint8_t)(7 - (x % 8));
|
||||
buf[idx] |= (uint8_t)(1u << bit);
|
||||
}
|
||||
|
||||
esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
|
||||
uint16_t src_width,
|
||||
uint16_t src_height,
|
||||
bool scale_to_width,
|
||||
uint8_t threshold,
|
||||
bool invert,
|
||||
uint16_t max_height,
|
||||
uint16_t *out_width,
|
||||
uint16_t *out_height,
|
||||
uint8_t **out_raster,
|
||||
size_t *out_len,
|
||||
char *err,
|
||||
size_t err_len) {
|
||||
if (gray == NULL || src_width == 0 || src_height == 0 ||
|
||||
out_width == NULL || out_height == NULL || out_raster == NULL || out_len == NULL) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "invalid args");
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (max_height == 0 || max_height > 3000) {
|
||||
max_height = 3000;
|
||||
}
|
||||
|
||||
uint16_t dst_width = RASTER_WIDTH;
|
||||
uint16_t dst_height = src_height;
|
||||
if (scale_to_width) {
|
||||
if (src_width == 0) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "invalid source width");
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint32_t scaled = ((uint32_t)src_height * RASTER_WIDTH + (src_width / 2u)) / src_width;
|
||||
if (scaled == 0) {
|
||||
scaled = 1;
|
||||
}
|
||||
if (scaled > max_height) {
|
||||
scaled = max_height;
|
||||
}
|
||||
dst_height = (uint16_t)scaled;
|
||||
} else if (src_width != RASTER_WIDTH) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "width must be 384 when scale_to_width=false");
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (dst_height == 0 || dst_height > max_height) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "invalid output height");
|
||||
}
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
size_t len = (size_t)RASTER_BYTES_PER_ROW * dst_height;
|
||||
uint8_t *raster = (uint8_t *)calloc(1, len);
|
||||
if (raster == NULL) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "no memory");
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
for (uint16_t y = 0; y < dst_height; ++y) {
|
||||
uint16_t src_y = scale_to_width
|
||||
? (uint16_t)(((uint32_t)y * src_height) / dst_height)
|
||||
: y;
|
||||
if (src_y >= src_height) {
|
||||
src_y = (uint16_t)(src_height - 1);
|
||||
}
|
||||
|
||||
for (uint16_t x = 0; x < RASTER_WIDTH; ++x) {
|
||||
uint16_t src_x = scale_to_width
|
||||
? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH)
|
||||
: x;
|
||||
if (src_x >= src_width) {
|
||||
src_x = (uint16_t)(src_width - 1);
|
||||
}
|
||||
|
||||
uint8_t v = gray[(size_t)src_y * src_width + src_x];
|
||||
bool black = invert ? (v > threshold) : (v < threshold);
|
||||
if (black) {
|
||||
set_black(raster, RASTER_WIDTH, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_width = dst_width;
|
||||
*out_height = dst_height;
|
||||
*out_raster = raster;
|
||||
*out_len = len;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static enum qrcodegen_Ecc map_qr_ecc(uint8_t ecc_level) {
|
||||
switch (ecc_level) {
|
||||
case 0:
|
||||
return qrcodegen_Ecc_LOW;
|
||||
case 1:
|
||||
return qrcodegen_Ecc_MEDIUM;
|
||||
case 2:
|
||||
return qrcodegen_Ecc_QUARTILE;
|
||||
case 3:
|
||||
default:
|
||||
return qrcodegen_Ecc_HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t raster_tools_render_qr_384(const char *text,
|
||||
uint8_t module_scale,
|
||||
uint8_t margin_modules,
|
||||
uint8_t ecc_level,
|
||||
uint16_t max_height,
|
||||
uint16_t *out_width,
|
||||
uint16_t *out_height,
|
||||
uint8_t **out_raster,
|
||||
size_t *out_len,
|
||||
char *err,
|
||||
size_t err_len) {
|
||||
if (text == NULL || text[0] == '\0' ||
|
||||
out_width == NULL || out_height == NULL || out_raster == NULL || out_len == NULL) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "invalid args");
|
||||
}
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (max_height == 0 || max_height > 3000) {
|
||||
max_height = 3000;
|
||||
}
|
||||
|
||||
if (margin_modules == 0) {
|
||||
margin_modules = 2;
|
||||
}
|
||||
|
||||
size_t qr_buf_len = qrcodegen_BUFFER_LEN_MAX;
|
||||
uint8_t *temp = (uint8_t *)malloc(qr_buf_len);
|
||||
uint8_t *qr = (uint8_t *)malloc(qr_buf_len);
|
||||
if (temp == NULL || qr == NULL) {
|
||||
free(temp);
|
||||
free(qr);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "no memory");
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
bool ok = qrcodegen_encodeText(text,
|
||||
temp,
|
||||
qr,
|
||||
map_qr_ecc(ecc_level),
|
||||
qrcodegen_VERSION_MIN,
|
||||
qrcodegen_VERSION_MAX,
|
||||
qrcodegen_Mask_AUTO,
|
||||
true);
|
||||
if (!ok) {
|
||||
free(temp);
|
||||
free(qr);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "qr encode failed");
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int qr_size = qrcodegen_getSize(qr);
|
||||
int full_modules = qr_size + 2 * margin_modules;
|
||||
|
||||
if (module_scale == 0) {
|
||||
module_scale = (uint8_t)(RASTER_WIDTH / full_modules);
|
||||
if (module_scale == 0) {
|
||||
module_scale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t image_size = (uint32_t)full_modules * module_scale;
|
||||
if (image_size > RASTER_WIDTH || image_size > max_height) {
|
||||
free(temp);
|
||||
free(qr);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "qr too large for 384 width");
|
||||
}
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uint16_t out_h = (uint16_t)image_size;
|
||||
size_t len = (size_t)RASTER_BYTES_PER_ROW * out_h;
|
||||
uint8_t *raster = (uint8_t *)calloc(1, len);
|
||||
if (raster == NULL) {
|
||||
free(temp);
|
||||
free(qr);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "no memory");
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint16_t x_origin = (uint16_t)((RASTER_WIDTH - image_size) / 2);
|
||||
for (int my = 0; my < full_modules; ++my) {
|
||||
for (int mx = 0; mx < full_modules; ++mx) {
|
||||
int qx = mx - margin_modules;
|
||||
int qy = my - margin_modules;
|
||||
bool black = (qx >= 0 && qx < qr_size && qy >= 0 && qy < qr_size)
|
||||
? qrcodegen_getModule(qr, qx, qy)
|
||||
: false;
|
||||
if (!black) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t px0 = (uint16_t)(x_origin + mx * module_scale);
|
||||
uint16_t py0 = (uint16_t)(my * module_scale);
|
||||
for (uint8_t sy = 0; sy < module_scale; ++sy) {
|
||||
uint16_t py = (uint16_t)(py0 + sy);
|
||||
if (py >= out_h) {
|
||||
continue;
|
||||
}
|
||||
for (uint8_t sx = 0; sx < module_scale; ++sx) {
|
||||
uint16_t px = (uint16_t)(px0 + sx);
|
||||
if (px < RASTER_WIDTH) {
|
||||
set_black(raster, RASTER_WIDTH, px, py);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(temp);
|
||||
free(qr);
|
||||
|
||||
*out_width = RASTER_WIDTH;
|
||||
*out_height = out_h;
|
||||
*out_raster = raster;
|
||||
*out_len = len;
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user