libptouch を複数ソースに分割し src/lib に配置
- core / usb / print / status / png と libptouch_internal.h に分割 - 旧単一ファイル src/libptouch.c を削除 - CMake のソース一覧と include パスを更新 - README・libptouch.h の参照パスを追随 Made-with: Cursor
This commit is contained in:
164
src/lib/libptouch_png.c
Normal file
164
src/lib/libptouch_png.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* libptouch — PNG → 1bit packed raster (libpng)
|
||||
*
|
||||
* Author: knb
|
||||
* Email: knb@artif.org
|
||||
*/
|
||||
|
||||
#include "libptouch_internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
void libptouch_free_raster(uint8_t *raster)
|
||||
{
|
||||
free(raster);
|
||||
}
|
||||
|
||||
libptouch_err_t libptouch_png_file_to_raster(libptouch_ctx *ctx, const char *path,
|
||||
const libptouch_png_options_t *options,
|
||||
uint8_t **out_raster, size_t *out_raster_bytes,
|
||||
libptouch_raster_params_t *out_params)
|
||||
{
|
||||
if (!ctx || !path || !out_raster || !out_raster_bytes || !out_params) {
|
||||
if (ctx)
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument");
|
||||
return LIBPTOUCH_ERR_ARG;
|
||||
}
|
||||
|
||||
uint8_t thr = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
|
||||
if (options)
|
||||
thr = options->threshold;
|
||||
|
||||
*out_raster = NULL;
|
||||
*out_raster_bytes = 0;
|
||||
out_params->width_dots = 0;
|
||||
out_params->height_dots = 0;
|
||||
out_params->margin_mm = 0;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
char buf[192];
|
||||
snprintf(buf, sizeof(buf), "open %s: %s", path, strerror(errno));
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IO, buf);
|
||||
return LIBPTOUCH_ERR_IO;
|
||||
}
|
||||
|
||||
unsigned char sig[8];
|
||||
if (fread(sig, 1, 8, fp) != 8) {
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "short read (not a PNG?)");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
if (png_sig_cmp(sig, 0, 8) != 0) {
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "not a PNG file");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
|
||||
png_structp png_ptr =
|
||||
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "png_create_read_struct failed");
|
||||
return LIBPTOUCH_ERR_NOMEM;
|
||||
}
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "png_create_info_struct failed");
|
||||
return LIBPTOUCH_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "PNG decode error");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
png_set_sig_bytes(png_ptr, 8);
|
||||
|
||||
const int transforms = (int)(PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16 |
|
||||
PNG_TRANSFORM_GRAY_TO_RGB);
|
||||
png_read_png(png_ptr, info_ptr, transforms, NULL);
|
||||
|
||||
png_uint_32 width = png_get_image_width(png_ptr, info_ptr);
|
||||
png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
|
||||
int channels = (int)png_get_channels(png_ptr, info_ptr);
|
||||
png_bytepp rows = png_get_rows(png_ptr, info_ptr);
|
||||
|
||||
if (width == 0 || height == 0 || !rows) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "invalid PNG dimensions");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
if (width > 100000u || height > 100000u) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "PNG dimensions too large");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
if (channels != 3 && channels != 4) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "unsupported PNG channel count");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
|
||||
size_t row_bytes = ((size_t)width + 7u) / 8u;
|
||||
if (height > SIZE_MAX / row_bytes) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "raster size overflow");
|
||||
return LIBPTOUCH_ERR_IMAGE;
|
||||
}
|
||||
size_t total = row_bytes * (size_t)height;
|
||||
uint8_t *out = (uint8_t *)calloc(1, total);
|
||||
if (!out) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "calloc raster failed");
|
||||
return LIBPTOUCH_ERR_NOMEM;
|
||||
}
|
||||
|
||||
for (png_uint_32 y = 0; y < height; y++) {
|
||||
const png_byte *row = rows[y];
|
||||
uint8_t *dst_row = out + (size_t)y * row_bytes;
|
||||
for (png_uint_32 x = 0; x < width; x++) {
|
||||
size_t o = (size_t)x * (size_t)channels;
|
||||
unsigned r = row[o + 0];
|
||||
unsigned g = row[o + 1];
|
||||
unsigned b = row[o + 2];
|
||||
unsigned a = channels == 4 ? row[o + 3] : 255u;
|
||||
if (a < 128u) {
|
||||
continue;
|
||||
}
|
||||
unsigned yv = (77u * r + 150u * g + 29u * b) >> 8;
|
||||
int black = (yv < (unsigned)thr) ? 1 : 0;
|
||||
if (!black)
|
||||
continue;
|
||||
size_t bit = (size_t)x % 8u;
|
||||
size_t byte = (size_t)x / 8u;
|
||||
dst_row[byte] |= (uint8_t)(1u << (7u - bit));
|
||||
}
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
|
||||
out_params->width_dots = (uint32_t)width;
|
||||
out_params->height_dots = (uint32_t)height;
|
||||
out_params->margin_mm = 0;
|
||||
*out_raster = out;
|
||||
*out_raster_bytes = total;
|
||||
ptouch_set_error(ctx, LIBPTOUCH_OK, "");
|
||||
return LIBPTOUCH_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user