z-image
This commit is contained in:
@@ -63,8 +63,18 @@ static const char *s_web_index =
|
||||
" <button id='btnPrintText'>Print Text</button>\n"
|
||||
" </div>\n"
|
||||
"\n"
|
||||
" <label>Image Upload</label>\n"
|
||||
" <input id='imageFile' type='file' accept='.png,image/png'>\n"
|
||||
" <label>Z-Image Prompt</label>\n"
|
||||
" <textarea id='imagePrompt'>一只坐在窗边的橘猫,午后阳光,胶片质感,写实风格。</textarea>\n"
|
||||
" <div class='row'>\n"
|
||||
" <select id='imgSize' style='max-width:180px'>\n"
|
||||
" <option value='1024*1024'>1024*1024</option>\n"
|
||||
" <option value='1120*1440' selected>1120*1440</option>\n"
|
||||
" <option value='1536*864'>1536*864</option>\n"
|
||||
" <option value='1536*1536'>1536*1536</option>\n"
|
||||
" </select>\n"
|
||||
" <input id='imgTimeout' type='number' min='5000' max='180000' value='45000' placeholder='Gen timeout ms' style='max-width:160px'>\n"
|
||||
" <input id='imgFetchTimeout' type='number' min='2000' max='120000' value='15000' placeholder='Fetch timeout ms' style='max-width:170px'>\n"
|
||||
" </div>\n"
|
||||
" <div class='row'>\n"
|
||||
" <input id='imgThreshold' type='number' min='0' max='255' value='160' placeholder='Threshold 0-255' style='max-width:160px'>\n"
|
||||
" <input id='imgMaxHeight' type='number' min='64' max='3000' value='2200' placeholder='Max height' style='max-width:160px'>\n"
|
||||
@@ -84,9 +94,13 @@ static const char *s_web_index =
|
||||
" <input id='imgInvert' type='checkbox' style='width:auto;'>\n"
|
||||
" Invert\n"
|
||||
" </label>\n"
|
||||
" <button id='btnPrintImage'>Print Image</button>\n"
|
||||
" <label style='margin:0;display:flex;align-items:center;gap:6px;'>\n"
|
||||
" <input id='imgPromptExtend' type='checkbox' style='width:auto;'>\n"
|
||||
" Prompt Extend\n"
|
||||
" </label>\n"
|
||||
" <button id='btnPrintImage'>Generate & Print</button>\n"
|
||||
" </div>\n"
|
||||
" <div class='small'>Direct PNG upload: browser sends binary PNG; ESP32-S3 decodes/scales/thresholds then prints.</div>\n"
|
||||
" <div class='small'>Submit prompt only: ESP32-S3 calls DashScope Z-Image, downloads PNG, rasterizes and prints.</div>\n"
|
||||
"\n"
|
||||
" <div class='small'>Tip: connect printer first, then submit print jobs.</div>\n"
|
||||
" <pre id='out'>Ready.</pre>\n"
|
||||
@@ -97,12 +111,16 @@ static const char *s_web_index =
|
||||
" const apiKeyEl = document.getElementById('apiKey');\n"
|
||||
" const printerNameEl = document.getElementById('printerName');\n"
|
||||
" const printTextEl = document.getElementById('printText');\n"
|
||||
" const imageFileEl = document.getElementById('imageFile');\n"
|
||||
" const imagePromptEl = document.getElementById('imagePrompt');\n"
|
||||
" const imgSizeEl = document.getElementById('imgSize');\n"
|
||||
" const imgTimeoutEl = document.getElementById('imgTimeout');\n"
|
||||
" const imgFetchTimeoutEl = document.getElementById('imgFetchTimeout');\n"
|
||||
" const imgThresholdEl = document.getElementById('imgThreshold');\n"
|
||||
" const imgMaxHeightEl = document.getElementById('imgMaxHeight');\n"
|
||||
" const imgDensityEl = document.getElementById('imgDensity');\n"
|
||||
" const imgScaleToWidthEl = document.getElementById('imgScaleToWidth');\n"
|
||||
" const imgInvertEl = document.getElementById('imgInvert');\n"
|
||||
" const imgPromptExtendEl = document.getElementById('imgPromptExtend');\n"
|
||||
"\n"
|
||||
" function log(obj) {\n"
|
||||
" if (typeof obj === 'string') {\n"
|
||||
@@ -128,41 +146,23 @@ static const char *s_web_index =
|
||||
" return n;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" function buildImageUploadRequest(file) {\n"
|
||||
" if (!file) {\n"
|
||||
" throw new Error('please select a png image first');\n"
|
||||
" function buildImagePromptRequest() {\n"
|
||||
" const prompt = imagePromptEl.value.trim();\n"
|
||||
" if (!prompt) {\n"
|
||||
" throw new Error('please input image prompt');\n"
|
||||
" }\n"
|
||||
" const name = (file.name || '').toLowerCase();\n"
|
||||
" const type = (file.type || '').toLowerCase();\n"
|
||||
" const isPng = type.includes('png') || name.endsWith('.png');\n"
|
||||
" if (!isPng) {\n"
|
||||
" throw new Error('only .png is supported in direct upload mode');\n"
|
||||
" }\n"
|
||||
" if (file.size <= 0) {\n"
|
||||
" throw new Error('empty image file');\n"
|
||||
" }\n"
|
||||
" if (file.size > 3 * 1024 * 1024) {\n"
|
||||
" throw new Error('png too large (>3MB), backend limit is 4MB');\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" const densityMap = {\n"
|
||||
" '较淡': 'light',\n"
|
||||
" '中等': 'medium',\n"
|
||||
" '较浓': 'dark',\n"
|
||||
" '最深': 'max'\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" const q = new URLSearchParams();\n"
|
||||
" q.set('scale_to_width', imgScaleToWidthEl.checked ? '1' : '0');\n"
|
||||
" q.set('threshold', String(toInt(imgThresholdEl.value, 160, 0, 255)));\n"
|
||||
" q.set('invert', imgInvertEl.checked ? '1' : '0');\n"
|
||||
" q.set('max_height', String(toInt(imgMaxHeightEl.value, 2200, 64, 3000)));\n"
|
||||
" q.set('density', densityMap[imgDensityEl.value] || 'medium');\n"
|
||||
"\n"
|
||||
" return {\n"
|
||||
" path: '/v1/print/image?' + q.toString(),\n"
|
||||
" body: file,\n"
|
||||
" contentType: file.type || 'image/png'\n"
|
||||
" prompt,\n"
|
||||
" size: imgSizeEl.value || '1120*1440',\n"
|
||||
" prompt_extend: imgPromptExtendEl.checked,\n"
|
||||
" threshold: toInt(imgThresholdEl.value, 160, 0, 255),\n"
|
||||
" max_height: toInt(imgMaxHeightEl.value, 2200, 64, 3000),\n"
|
||||
" density: imgDensityEl.value || '中等',\n"
|
||||
" scale_to_width: imgScaleToWidthEl.checked,\n"
|
||||
" invert: imgInvertEl.checked,\n"
|
||||
" timeout_ms: toInt(imgTimeoutEl.value, 45000, 5000, 180000),\n"
|
||||
" fetch_timeout_ms: toInt(imgFetchTimeoutEl.value, 15000, 2000, 120000)\n"
|
||||
" };\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
@@ -179,19 +179,6 @@ static const char *s_web_index =
|
||||
" return data;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" async function callBinaryApi(path, body, contentType) {\n"
|
||||
" const h = headers(false);\n"
|
||||
" h['Content-Type'] = contentType || 'application/octet-stream';\n"
|
||||
" const r = await fetch(path, { method: 'POST', headers: h, body });\n"
|
||||
" const text = await r.text();\n"
|
||||
" let data = text;\n"
|
||||
" try { data = JSON.parse(text); } catch (_) {}\n"
|
||||
" if (!r.ok) {\n"
|
||||
" throw { status: r.status, data };\n"
|
||||
" }\n"
|
||||
" return data;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" async function run(fn) {\n"
|
||||
" try {\n"
|
||||
" const data = await fn();\n"
|
||||
@@ -226,11 +213,8 @@ static const char *s_web_index =
|
||||
" }));\n"
|
||||
"\n"
|
||||
" document.getElementById('btnPrintImage').onclick = () => run(async () => {\n"
|
||||
" const file = imageFileEl.files && imageFileEl.files[0] ? imageFileEl.files[0] : null;\n"
|
||||
" const req = buildImageUploadRequest(file);\n"
|
||||
" const result = await callBinaryApi(req.path, req.body, req.contentType);\n"
|
||||
" result.client_note = 'uploaded png bytes: ' + file.size;\n"
|
||||
" return result;\n"
|
||||
" const req = buildImagePromptRequest();\n"
|
||||
" return callApi('/v1/print/image', 'POST', req);\n"
|
||||
" });\n"
|
||||
" </script>\n"
|
||||
"</body>\n"
|
||||
@@ -252,6 +236,7 @@ esp_err_t rest_server_start(void) {
|
||||
config.server_port = CONFIG_TQ_HTTP_PORT;
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
config.max_uri_handlers = 32;
|
||||
config.stack_size = 10240;
|
||||
|
||||
esp_err_t err = httpd_start(&s_server, &config);
|
||||
if (err != ESP_OK) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "image_generation.h"
|
||||
#include "printer_protocol.h"
|
||||
#include "raster_tools.h"
|
||||
static esp_err_t submit_raster_job_and_reply(httpd_req_t *req,
|
||||
@@ -154,6 +155,194 @@ static void parse_image_upload_options(httpd_req_t *req,
|
||||
free(query);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool scale_to_width;
|
||||
uint8_t threshold;
|
||||
bool invert;
|
||||
uint16_t max_height;
|
||||
const char *density;
|
||||
} image_print_options_t;
|
||||
|
||||
typedef struct {
|
||||
char prompt[801];
|
||||
char size[24];
|
||||
bool has_size;
|
||||
bool prompt_extend;
|
||||
bool has_seed;
|
||||
uint32_t seed;
|
||||
uint32_t timeout_ms;
|
||||
uint32_t fetch_timeout_ms;
|
||||
} image_generate_options_t;
|
||||
|
||||
static void image_print_options_set_defaults(image_print_options_t *out) {
|
||||
if (out == NULL) {
|
||||
return;
|
||||
}
|
||||
out->scale_to_width = true;
|
||||
out->threshold = 160;
|
||||
out->invert = false;
|
||||
out->max_height = 2200;
|
||||
out->density = "中等";
|
||||
}
|
||||
|
||||
static void parse_image_print_options_from_json(cJSON *json, image_print_options_t *out) {
|
||||
if (json == NULL || out == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *jscale = cJSON_GetObjectItemCaseSensitive(json, "scale_to_width");
|
||||
cJSON *jthreshold = cJSON_GetObjectItemCaseSensitive(json, "threshold");
|
||||
cJSON *jinvert = cJSON_GetObjectItemCaseSensitive(json, "invert");
|
||||
cJSON *jmaxh = cJSON_GetObjectItemCaseSensitive(json, "max_height");
|
||||
cJSON *jdensity = cJSON_GetObjectItemCaseSensitive(json, "density");
|
||||
|
||||
out->scale_to_width = rest_server_json_bool_with_default(jscale, out->scale_to_width);
|
||||
out->invert = rest_server_json_bool_with_default(jinvert, out->invert);
|
||||
if (cJSON_IsNumber(jthreshold) && jthreshold->valuedouble >= 0 && jthreshold->valuedouble <= 255) {
|
||||
out->threshold = (uint8_t)jthreshold->valuedouble;
|
||||
}
|
||||
if (cJSON_IsNumber(jmaxh) && jmaxh->valuedouble >= 64 && jmaxh->valuedouble <= 3000) {
|
||||
out->max_height = (uint16_t)jmaxh->valuedouble;
|
||||
}
|
||||
if (cJSON_IsString(jdensity) && jdensity->valuestring != NULL) {
|
||||
out->density = map_density_text(jdensity->valuestring);
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_image_size_text(const char *text, uint32_t *out_w, uint32_t *out_h) {
|
||||
if (text == NULL || out_w == NULL || out_h == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *sep = strchr(text, '*');
|
||||
if (sep == NULL || sep == text || *(sep + 1) == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
char wbuf[12] = {0};
|
||||
char hbuf[12] = {0};
|
||||
size_t wlen = (size_t)(sep - text);
|
||||
size_t hlen = strlen(sep + 1);
|
||||
if (wlen == 0 || hlen == 0 || wlen >= sizeof(wbuf) || hlen >= sizeof(hbuf)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(wbuf, text, wlen);
|
||||
memcpy(hbuf, sep + 1, hlen);
|
||||
wbuf[wlen] = '\0';
|
||||
hbuf[hlen] = '\0';
|
||||
|
||||
char *wend = NULL;
|
||||
char *hend = NULL;
|
||||
long w = strtol(wbuf, &wend, 10);
|
||||
long h = strtol(hbuf, &hend, 10);
|
||||
if (wend == wbuf || hend == hbuf || *wend != '\0' || *hend != '\0') {
|
||||
return false;
|
||||
}
|
||||
if (w <= 0 || h <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_w = (uint32_t)w;
|
||||
*out_h = (uint32_t)h;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_image_generation_options(cJSON *json,
|
||||
image_generate_options_t *out,
|
||||
char *err,
|
||||
size_t err_len) {
|
||||
if (json == NULL || out == NULL) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "invalid args");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->timeout_ms = CONFIG_TQ_Z_IMAGE_TIMEOUT_MS;
|
||||
out->fetch_timeout_ms = CONFIG_TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS;
|
||||
|
||||
cJSON *jprompt = cJSON_GetObjectItemCaseSensitive(json, "prompt");
|
||||
if (!cJSON_IsString(jprompt) || jprompt->valuestring == NULL || jprompt->valuestring[0] == '\0') {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "prompt is required");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t prompt_len = strlen(jprompt->valuestring);
|
||||
if (prompt_len > 800) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "prompt too long, max 800 chars");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
strlcpy(out->prompt, jprompt->valuestring, sizeof(out->prompt));
|
||||
|
||||
cJSON *jsize = cJSON_GetObjectItemCaseSensitive(json, "size");
|
||||
if (cJSON_IsString(jsize) && jsize->valuestring != NULL && jsize->valuestring[0] != '\0') {
|
||||
uint32_t w = 0;
|
||||
uint32_t h = 0;
|
||||
if (!parse_image_size_text(jsize->valuestring, &w, &h)) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "size must be like 1120*1440");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t pixels = (uint64_t)w * (uint64_t)h;
|
||||
if (pixels < (512ULL * 512ULL) || pixels > (2048ULL * 2048ULL)) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "size pixels must be in [512*512, 2048*2048]");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
out->has_size = true;
|
||||
strlcpy(out->size, jsize->valuestring, sizeof(out->size));
|
||||
}
|
||||
|
||||
cJSON *jextend = cJSON_GetObjectItemCaseSensitive(json, "prompt_extend");
|
||||
out->prompt_extend = rest_server_json_bool_with_default(jextend, false);
|
||||
|
||||
cJSON *jseed = cJSON_GetObjectItemCaseSensitive(json, "seed");
|
||||
if (cJSON_IsNumber(jseed)) {
|
||||
if (jseed->valuedouble < 0 || jseed->valuedouble > 2147483647.0) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "seed must be in [0, 2147483647]");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
out->has_seed = true;
|
||||
out->seed = (uint32_t)jseed->valuedouble;
|
||||
}
|
||||
|
||||
cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms");
|
||||
if (cJSON_IsNumber(jtimeout)) {
|
||||
if (jtimeout->valuedouble < 5000 || jtimeout->valuedouble > 180000) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "timeout_ms must be 5000..180000");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
out->timeout_ms = (uint32_t)jtimeout->valuedouble;
|
||||
}
|
||||
|
||||
cJSON *jfetch_timeout = cJSON_GetObjectItemCaseSensitive(json, "fetch_timeout_ms");
|
||||
if (cJSON_IsNumber(jfetch_timeout)) {
|
||||
if (jfetch_timeout->valuedouble < 2000 || jfetch_timeout->valuedouble > 120000) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "fetch_timeout_ms must be 2000..120000");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
out->fetch_timeout_ms = (uint32_t)jfetch_timeout->valuedouble;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static esp_err_t decode_image_json_to_raster(cJSON *json,
|
||||
uint16_t *out_width,
|
||||
uint16_t *out_height,
|
||||
@@ -341,21 +530,26 @@ esp_err_t rest_server_print_raster_post(httpd_req_t *req) {
|
||||
return submit_rc;
|
||||
}
|
||||
|
||||
esp_err_t rest_server_print_image_post(httpd_req_t *req) {
|
||||
if (!rest_server_auth_ok(req)) {
|
||||
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
|
||||
static esp_err_t rest_server_send_image_generation_error(httpd_req_t *req,
|
||||
esp_err_t rc,
|
||||
const char *msg) {
|
||||
const char *message = (msg != NULL && msg[0] != '\0') ? msg : "image generation failed";
|
||||
if (rc == ESP_ERR_INVALID_ARG) {
|
||||
return rest_server_send_error(req, "400 Bad Request", message);
|
||||
}
|
||||
|
||||
char content_type[96] = {0};
|
||||
if (httpd_req_get_hdr_value_str(req, "Content-Type", content_type, sizeof(content_type)) == ESP_OK) {
|
||||
if (strstr(content_type, "application/json") != NULL) {
|
||||
return rest_server_send_error(req, "400 Bad Request", "use binary png body, not json");
|
||||
}
|
||||
if (strstr(content_type, "image/png") == NULL) {
|
||||
return rest_server_send_error(req, "415 Unsupported Media Type", "Content-Type must be image/png");
|
||||
}
|
||||
if (rc == ESP_ERR_INVALID_STATE) {
|
||||
return rest_server_send_error(req, "503 Service Unavailable", message);
|
||||
}
|
||||
if (rc == ESP_ERR_TIMEOUT) {
|
||||
return rest_server_send_error(req, "504 Gateway Timeout", message);
|
||||
}
|
||||
if (rc == ESP_ERR_NO_MEM) {
|
||||
return rest_server_send_error(req, "500 Internal Server Error", message);
|
||||
}
|
||||
return rest_server_send_error(req, "502 Bad Gateway", message);
|
||||
}
|
||||
|
||||
static esp_err_t rest_server_print_image_binary(httpd_req_t *req) {
|
||||
bool scale_to_width = true;
|
||||
uint8_t threshold = 160;
|
||||
bool invert = false;
|
||||
@@ -409,6 +603,143 @@ esp_err_t rest_server_print_image_post(httpd_req_t *req) {
|
||||
return submit_rc;
|
||||
}
|
||||
|
||||
static esp_err_t rest_server_print_image_generate(httpd_req_t *req) {
|
||||
char *body = NULL;
|
||||
cJSON *json = NULL;
|
||||
image_generation_result_t *gen_result = NULL;
|
||||
uint8_t *raster = NULL;
|
||||
char request_id[80] = {0};
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
esp_err_t body_err = rest_server_read_body(req, &body);
|
||||
if (body_err != ESP_OK) {
|
||||
return rest_server_send_error(req, "400 Bad Request", "invalid request body");
|
||||
}
|
||||
|
||||
json = cJSON_Parse(body);
|
||||
free(body);
|
||||
body = NULL;
|
||||
if (json == NULL) {
|
||||
return rest_server_send_error(req, "400 Bad Request", "invalid json");
|
||||
}
|
||||
|
||||
image_generate_options_t gen_opt = {0};
|
||||
char parse_err[128] = {0};
|
||||
if (!parse_image_generation_options(json, &gen_opt, parse_err, sizeof(parse_err))) {
|
||||
ret = rest_server_send_error(req,
|
||||
"400 Bad Request",
|
||||
parse_err[0] != '\0' ? parse_err : "invalid image options");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
image_print_options_t print_opt = {0};
|
||||
image_print_options_set_defaults(&print_opt);
|
||||
parse_image_print_options_from_json(json, &print_opt);
|
||||
cJSON_Delete(json);
|
||||
json = NULL;
|
||||
|
||||
image_generation_request_t gen_req = {
|
||||
.prompt = gen_opt.prompt,
|
||||
.size = gen_opt.has_size ? gen_opt.size : NULL,
|
||||
.prompt_extend = gen_opt.prompt_extend,
|
||||
.has_seed = gen_opt.has_seed,
|
||||
.seed = gen_opt.seed,
|
||||
.generation_timeout_ms = gen_opt.timeout_ms,
|
||||
.download_timeout_ms = gen_opt.fetch_timeout_ms,
|
||||
};
|
||||
|
||||
gen_result = (image_generation_result_t *)calloc(1, sizeof(*gen_result));
|
||||
if (gen_result == NULL) {
|
||||
ret = rest_server_send_error(req, "500 Internal Server Error", "no memory");
|
||||
goto cleanup;
|
||||
}
|
||||
image_generation_result_reset(gen_result);
|
||||
|
||||
char model_err[160] = {0};
|
||||
esp_err_t gen_rc = image_generation_generate_png(&gen_req,
|
||||
gen_result,
|
||||
model_err,
|
||||
sizeof(model_err));
|
||||
if (gen_rc != ESP_OK) {
|
||||
ret = rest_server_send_image_generation_error(req, gen_rc, model_err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (gen_result->request_id[0] != '\0') {
|
||||
strlcpy(request_id, gen_result->request_id, sizeof(request_id));
|
||||
}
|
||||
|
||||
uint16_t width = 0;
|
||||
uint16_t height = 0;
|
||||
size_t raster_len = 0;
|
||||
char decode_err[128] = {0};
|
||||
esp_err_t decode_rc = raster_tools_convert_png_to_raster_384(gen_result->png,
|
||||
gen_result->png_len,
|
||||
print_opt.scale_to_width,
|
||||
print_opt.threshold,
|
||||
print_opt.invert,
|
||||
print_opt.max_height,
|
||||
&width,
|
||||
&height,
|
||||
&raster,
|
||||
&raster_len,
|
||||
decode_err,
|
||||
sizeof(decode_err));
|
||||
if (decode_rc != ESP_OK) {
|
||||
ret = rest_server_send_error(req,
|
||||
"400 Bad Request",
|
||||
decode_err[0] != '\0' ? decode_err : "decode generated image failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char warning[160] = {0};
|
||||
if (request_id[0] != '\0') {
|
||||
snprintf(warning, sizeof(warning), "z-image request_id=%s", request_id);
|
||||
}
|
||||
|
||||
ret = submit_raster_job_and_reply(req,
|
||||
raster,
|
||||
raster_len,
|
||||
width,
|
||||
height,
|
||||
print_opt.density,
|
||||
warning);
|
||||
|
||||
cleanup:
|
||||
if (body != NULL) {
|
||||
free(body);
|
||||
}
|
||||
if (json != NULL) {
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
if (raster != NULL) {
|
||||
free(raster);
|
||||
}
|
||||
if (gen_result != NULL) {
|
||||
image_generation_result_free(gen_result);
|
||||
free(gen_result);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t rest_server_print_image_post(httpd_req_t *req) {
|
||||
if (!rest_server_auth_ok(req)) {
|
||||
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
|
||||
}
|
||||
|
||||
char content_type[96] = {0};
|
||||
if (httpd_req_get_hdr_value_str(req, "Content-Type", content_type, sizeof(content_type)) == ESP_OK) {
|
||||
if (strstr(content_type, "application/json") != NULL) {
|
||||
return rest_server_print_image_generate(req);
|
||||
}
|
||||
if (strstr(content_type, "image/png") == NULL) {
|
||||
return rest_server_send_error(req, "415 Unsupported Media Type", "Content-Type must be application/json or image/png");
|
||||
}
|
||||
}
|
||||
|
||||
return rest_server_print_image_binary(req);
|
||||
}
|
||||
|
||||
esp_err_t rest_server_print_qr_post(httpd_req_t *req) {
|
||||
if (!rest_server_auth_ok(req)) {
|
||||
return rest_server_send_error(req, "401 Unauthorized", "unauthorized");
|
||||
|
||||
Reference in New Issue
Block a user