Files
ptouch_label/src/lib/libptouch_png.c
knb add08ba9b2 libptouch を複数ソースに分割し src/lib に配置
- core / usb / print / status / png と libptouch_internal.h に分割
- 旧単一ファイル src/libptouch.c を削除
- CMake のソース一覧と include パスを更新
- README・libptouch.h の参照パスを追随

Made-with: Cursor
2026-04-13 09:43:42 +09:00

165 lines
4.6 KiB
C

/*
* 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;
}