149 lines
4.0 KiB
C
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;
|
|
}
|