jpg 打印

This commit is contained in:
admin
2026-02-24 18:51:53 +08:00
parent 9e141b8de0
commit e1e0ac8929
10 changed files with 535 additions and 127 deletions

View File

@@ -31,6 +31,19 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
char *err,
size_t err_len);
esp_err_t raster_tools_convert_jpeg_to_raster_384(const uint8_t *jpeg,
size_t jpeg_len,
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);
esp_err_t raster_tools_render_qr_384(const char *text,
uint8_t module_scale,
uint8_t margin_modules,

View File

@@ -5,10 +5,14 @@
#include <stdlib.h>
#include <string.h>
#include "esp_heap_caps.h"
#include "jpeg_decoder.h"
#include "qrcodegen.h"
#define RASTER_WIDTH 384
#define RASTER_BYTES_PER_ROW (RASTER_WIDTH / 8)
#define JPEG_DECODE_MAX_BYTES (2 * 1024 * 1024)
#define JPEG_DECODE_MAX_WIDTH 1600
static void set_black(uint8_t *buf, uint16_t width, uint16_t x, uint16_t y) {
if (x >= width) {
@@ -20,6 +24,63 @@ static void set_black(uint8_t *buf, uint16_t width, uint16_t x, uint16_t y) {
buf[idx] |= (uint8_t)(1u << bit);
}
static void *alloc_prefer_psram(size_t size) {
void *ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (ptr == NULL) {
ptr = malloc(size);
}
return ptr;
}
static void *calloc_prefer_psram(size_t n, size_t size) {
void *ptr = heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (ptr == NULL) {
ptr = calloc(n, size);
}
return ptr;
}
static uint32_t jpeg_scale_div(esp_jpeg_image_scale_t scale) {
switch (scale) {
case JPEG_IMAGE_SCALE_1_2:
return 2;
case JPEG_IMAGE_SCALE_1_4:
return 4;
case JPEG_IMAGE_SCALE_1_8:
return 8;
case JPEG_IMAGE_SCALE_0:
default:
return 1;
}
}
static esp_jpeg_image_scale_t choose_jpeg_scale(uint16_t src_width,
uint16_t src_height,
bool scale_to_width) {
if (!scale_to_width) {
return JPEG_IMAGE_SCALE_0;
}
const esp_jpeg_image_scale_t scales[] = {
JPEG_IMAGE_SCALE_0,
JPEG_IMAGE_SCALE_1_2,
JPEG_IMAGE_SCALE_1_4,
JPEG_IMAGE_SCALE_1_8
};
for (size_t i = 0; i < sizeof(scales) / sizeof(scales[0]); ++i) {
uint32_t div = jpeg_scale_div(scales[i]);
uint32_t w = (uint32_t)src_width / div;
uint32_t h = (uint32_t)src_height / div;
size_t bytes = (size_t)w * h * 2u; /* RGB565 */
if (w > 0 && h > 0 && w <= JPEG_DECODE_MAX_WIDTH && bytes <= JPEG_DECODE_MAX_BYTES) {
return scales[i];
}
}
return JPEG_IMAGE_SCALE_1_8;
}
esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
uint16_t src_width,
uint16_t src_height,
@@ -77,7 +138,7 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
}
size_t len = (size_t)RASTER_BYTES_PER_ROW * dst_height;
uint8_t *raster = (uint8_t *)calloc(1, len);
uint8_t *raster = (uint8_t *)calloc_prefer_psram(1, len);
if (raster == NULL) {
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "no memory");
@@ -116,6 +177,191 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
return ESP_OK;
}
esp_err_t raster_tools_convert_jpeg_to_raster_384(const uint8_t *jpeg,
size_t jpeg_len,
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 (jpeg == NULL || jpeg_len == 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;
}
*out_width = 0;
*out_height = 0;
*out_raster = NULL;
*out_len = 0;
if (jpeg_len > UINT32_MAX) {
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "jpeg too large");
}
return ESP_ERR_INVALID_SIZE;
}
if (max_height == 0 || max_height > 3000) {
max_height = 3000;
}
esp_jpeg_image_cfg_t info_cfg = {
.indata = (uint8_t *)jpeg,
.indata_size = (uint32_t)jpeg_len,
.out_format = JPEG_IMAGE_FORMAT_RGB565,
.out_scale = JPEG_IMAGE_SCALE_0,
};
esp_jpeg_image_output_t info = {0};
esp_err_t info_rc = esp_jpeg_get_image_info(&info_cfg, &info);
if (info_rc != ESP_OK || info.width == 0 || info.height == 0) {
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "invalid jpeg");
}
return ESP_FAIL;
}
esp_jpeg_image_scale_t decode_scale = choose_jpeg_scale(info.width, info.height, scale_to_width);
uint32_t scale_div = jpeg_scale_div(decode_scale);
uint32_t dec_w = (uint32_t)info.width / scale_div;
uint32_t dec_h = (uint32_t)info.height / scale_div;
size_t decoded_pixels = (size_t)dec_w * dec_h;
if (decoded_pixels == 0 || decoded_pixels > (SIZE_MAX / 2u)) {
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "jpeg size overflow");
}
return ESP_ERR_INVALID_SIZE;
}
size_t rgb565_len = decoded_pixels * 2u;
if (rgb565_len > UINT32_MAX) {
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "jpeg output too large");
}
return ESP_ERR_INVALID_SIZE;
}
uint8_t *rgb565 = (uint8_t *)alloc_prefer_psram(rgb565_len);
if (rgb565 == NULL) {
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "no memory");
}
return ESP_ERR_NO_MEM;
}
esp_jpeg_image_cfg_t decode_cfg = {
.indata = (uint8_t *)jpeg,
.indata_size = (uint32_t)jpeg_len,
.outbuf = rgb565,
.outbuf_size = (uint32_t)rgb565_len,
.out_format = JPEG_IMAGE_FORMAT_RGB565,
.out_scale = decode_scale,
};
esp_jpeg_image_output_t outimg = {0};
esp_err_t decode_rc = esp_jpeg_decode(&decode_cfg, &outimg);
if (decode_rc != ESP_OK || outimg.width == 0 || outimg.height == 0) {
free(rgb565);
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "jpeg decode failed");
}
return ESP_FAIL;
}
size_t expected_rgb565_len = (size_t)outimg.width * outimg.height * 2u;
if (outimg.output_len < expected_rgb565_len || expected_rgb565_len > rgb565_len) {
free(rgb565);
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "jpeg decode size mismatch");
}
return ESP_ERR_INVALID_SIZE;
}
uint16_t src_width = outimg.width;
uint16_t src_height = outimg.height;
uint16_t dst_width = RASTER_WIDTH;
uint16_t dst_height = src_height;
if (scale_to_width) {
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) {
free(rgb565);
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) {
free(rgb565);
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "invalid output height");
}
return ESP_ERR_INVALID_SIZE;
}
size_t raster_len = (size_t)RASTER_BYTES_PER_ROW * dst_height;
uint8_t *raster = (uint8_t *)calloc_prefer_psram(1, raster_len);
if (raster == NULL) {
free(rgb565);
if (err != NULL && err_len > 0) {
snprintf(err, err_len, "no memory");
}
return ESP_ERR_NO_MEM;
}
const uint16_t *src565 = (const uint16_t *)rgb565;
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);
}
uint16_t p = src565[(size_t)src_y * src_width + src_x];
uint8_t r5 = (uint8_t)((p >> 11) & 0x1Fu);
uint8_t g6 = (uint8_t)((p >> 5) & 0x3Fu);
uint8_t b5 = (uint8_t)(p & 0x1Fu);
uint8_t r8 = (uint8_t)((r5 * 527u + 23u) >> 6);
uint8_t g8 = (uint8_t)((g6 * 259u + 33u) >> 6);
uint8_t b8 = (uint8_t)((b5 * 527u + 23u) >> 6);
uint8_t v = (uint8_t)((77u * r8 + 150u * g8 + 29u * b8) >> 8);
bool black = invert ? (v > threshold) : (v < threshold);
if (black) {
set_black(raster, RASTER_WIDTH, x, y);
}
}
}
free(rgb565);
*out_width = dst_width;
*out_height = dst_height;
*out_raster = raster;
*out_len = raster_len;
return ESP_OK;
}
static enum qrcodegen_Ecc map_qr_ecc(uint8_t ecc_level) {
switch (ecc_level) {
case 0:
@@ -208,7 +454,7 @@ esp_err_t raster_tools_render_qr_384(const char *text,
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);
uint8_t *raster = (uint8_t *)calloc_prefer_psram(1, len);
if (raster == NULL) {
free(temp);
free(qr);