/* * libptouch — PNG → 1bit packed raster (libpng) * * Author: knb * Email: knb@artif.org */ #include "libptouch_internal.h" #include #include #include #include #include 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; }