#include "raster_tools.h" #include #include #include #include #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; }