suwi/utl/picmake.c
2025-11-11 23:15:54 +01:00

149 lines
4.0 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <getopt.h>
#include <png.h>
static char const pic_sig[8] = {'\xdd', '\xdd', 'p', 'i', 'c', '\n', '\0', 0};
static char const help_text[] = "%s [-fh] [-o OUTPUT.dat] INPUT.png\n";
static void write_vlq(size_t value, FILE *file) {
unsigned char buf[9];
size_t bytes;
for (bytes = 0; bytes < sizeof (size_t); bytes++) {
size_t byte = value >> (bytes * 8);
if ((byte & (0x7f >> bytes)) == byte) {
break;
}
buf[8 - bytes] = value >> (bytes * 8);
}
buf[8 - bytes] = (value >> (bytes * 8)) | (0xff00 >> bytes);
fwrite(buf + 8 - bytes, 1, bytes + 1, file);
}
int main(int argc, char **argv) {
struct option const options[] = {
{"force", 0, NULL, 'f'},
{"help", 0, NULL, 'h'},
{"output", 1, NULL, 'o'},
{"verbose", 0, NULL, 'v'},
{NULL, 0, NULL, 0 },
};
int opt, force = 0, verbose = 0;
char *outname = NULL;
while ((opt = getopt_long(argc, argv, "fho:v", options, NULL)) != -1) {
switch (opt) {
case 'f':
force++;
break;
case 'h':
fprintf(stderr, help_text, argv[0]);
fputs("-f --force allow outputting not-quite-successful conversions\n", stderr);
fputs("-h --help this message\n", stderr);
fputs("-o --output=FILE output converted image to FILE\n", stderr);
fputs("-v --verbose print diagnostics\n", stderr);
return EXIT_SUCCESS;
case 'o':
outname = optarg;
break;
case 'v':
verbose++;
break;
default:
return EXIT_FAILURE;
}
}
if (optind + 1 != argc) {
fprintf(stderr, help_text, argv[0]);
return EXIT_FAILURE;
}
// half-assed libpng use, will be fixed in a project where proper libpng use is necessary
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, NULL, NULL);
if (png_ptr == NULL) {
return EXIT_FAILURE;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
return EXIT_FAILURE;
}
FILE *fp = NULL;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if (fp != NULL) {
fclose(fp);
}
return EXIT_FAILURE;
}
fp = fopen(argv[optind], "rb");
if (fp == NULL) {
perror(argv[optind]);
return EXIT_FAILURE;
}
png_init_io(png_ptr, fp);
// we do NOT want gamma!
//png_set_gamma_fixed(png_ptr, PNG_GAMMA_LINEAR, PNG_GAMMA_LINEAR);
//png_set_alpha_mode(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_LINEAR);
png_read_info(png_ptr, info_ptr);
png_uint_32 width, height;
int bit_depth, color_type;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
} else if (bit_depth < 8) {
png_set_packing(png_ptr);
} else if (bit_depth > 8) {
png_set_scale_16(png_ptr);
}
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png_ptr);
} else {
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
}
png_set_rgb_to_gray(png_ptr, 1, -1, -1);
//png_set_expand_gray_1_2_4_to_8(png_ptr);
png_read_update_info(png_ptr, info_ptr);
png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
png_bytepp row_pointers = malloc(sizeof (png_bytep) * height);
unsigned char *image = malloc(rowbytes * height);
for (unsigned i = 0; i < height; i++) {
row_pointers[i] = image + (height - i - 1) * rowbytes;
}
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
//free(row_pointers);
if (outname != NULL) {
FILE *out = fopen(outname, "wb");
if (out == NULL) {
perror(outname);
return EXIT_FAILURE;
}
fwrite(pic_sig, 1, sizeof (pic_sig), out);
write_vlq(width - 1, out);
write_vlq(height - 1, out);
for (size_t i = 0; i < width * height; i++) {
if (image[i * 2 + 1] < 0x80) {
fputc(0x00, out);
} else if (image[i * 2 + 0] < 0x80) {
fputc(0x80, out);
} else {
fputc(0xff, out);
}
}
fclose(out);
}
return EXIT_SUCCESS;
}