CFLAGS="-lpng" make pnglineWindows binary:
CFLAGS="-lpng" make pnglineWindows binary:
| #include <png.h> | |
| #include <stdio.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <getopt.h> | |
| #include <string.h> | |
| #include <fcntl.h> | |
| const int buf = 256; | |
| const char header[] = "MSG"; | |
| int mode_encode(int factor) { | |
| int ret = 0; | |
| size_t siz = 0; | |
| size_t cap = buf*4; | |
| uint8_t *data = malloc(cap); | |
| memmove(data, header, sizeof(header)); | |
| siz += sizeof(header); | |
| if (!data) { | |
| return -1; | |
| } | |
| while (1) { | |
| if (siz+buf > cap) { | |
| cap <<= 1; | |
| uint8_t *newdata = realloc(data, cap); | |
| if (!newdata) { | |
| ret = -1; | |
| goto cleanup_data; | |
| } | |
| data = newdata; | |
| } | |
| int got = fread(data+siz, 1, buf, stdin); | |
| siz += got; | |
| if (got != buf) { | |
| break; | |
| } | |
| } | |
| if (!feof(stdin)) { | |
| perror("reading stdin"); | |
| ret = -1; | |
| goto cleanup_data; | |
| } | |
| png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
| png_infop info = NULL; | |
| if (!png) { | |
| ret = -1; | |
| goto cleanup_data; | |
| } | |
| info = png_create_info_struct(png); | |
| if (!info) { | |
| ret = -1; | |
| goto cleanup_png; | |
| } | |
| if (setjmp(png_jmpbuf(png))) { | |
| ret = -1; | |
| goto cleanup_png; | |
| } | |
| png_init_io(png, stdout); | |
| int reminder = (siz % 4); | |
| for (int i = 4 - reminder; i >= 0; i--) { | |
| data[siz+i] = 0; | |
| } | |
| int width = siz / 4 + (reminder && 1); | |
| int height = 1; | |
| png_set_IHDR( | |
| png, | |
| info, | |
| width * factor, | |
| height * factor, | |
| 8, | |
| siz < 256 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGBA, | |
| PNG_INTERLACE_NONE, | |
| PNG_COMPRESSION_TYPE_DEFAULT, | |
| PNG_FILTER_TYPE_DEFAULT | |
| ); | |
| png_bytep row = NULL; | |
| png_color *palette = NULL; | |
| png_byte *trans = NULL; | |
| if (siz < 256) { | |
| row = png_malloc(png, width * factor * sizeof(png_byte)); | |
| if (!row) { | |
| ret = -1; | |
| goto cleanup_png; | |
| } | |
| palette = png_malloc(png, width * sizeof(png_color)); | |
| if (!row) { | |
| ret = -1; | |
| goto cleanup_png_row; | |
| } | |
| trans = png_malloc(png, width * sizeof(png_byte)); | |
| if (!row) { | |
| ret = -1; | |
| goto cleanup_png_palette; | |
| } | |
| for (int i = 0; i < width; i++) { | |
| palette[i].red = data[i*4+1]; | |
| palette[i].green = data[i*4+2]; | |
| palette[i].blue = data[i*4+3]; | |
| trans[i] = data[i*4+0]; | |
| } | |
| for (int i = 0; i < width; i++) { | |
| for (int f = 0; f < factor; f++) { | |
| row[i*factor+f] = i; | |
| } | |
| } | |
| png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); | |
| png_set_PLTE(png, info, palette, width); | |
| png_set_tRNS(png, info, trans, width, NULL); | |
| } else { | |
| row = png_malloc(png, width * factor * 4 * sizeof(png_byte)); | |
| for (int i = 0; i < width; i++) { | |
| for (int f = 0; f < factor; f++) { | |
| row[i*factor*4+f*4+0] = data[i*4+1]; | |
| row[i*factor*4+f*4+1] = data[i*4+2]; | |
| row[i*factor*4+f*4+2] = data[i*4+3]; | |
| row[i*factor*4+f*4+3] = data[i*4+0]; | |
| } | |
| } | |
| } | |
| png_write_info(png, info); | |
| for (int i = 0; i < height * factor; i++) { | |
| png_write_row(png, row); | |
| } | |
| png_write_end(png, NULL); | |
| png_free(png, trans); | |
| cleanup_png_palette: | |
| png_free(png, palette); | |
| cleanup_png_row: | |
| png_free(png, row); | |
| cleanup_png: | |
| png_destroy_write_struct(&png, &info); | |
| cleanup_data: | |
| free(data); | |
| return ret; | |
| } | |
| int mode_decode() { | |
| int ret = 0; | |
| png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
| if(!png) { | |
| return -1; | |
| } | |
| png_infop info = png_create_info_struct(png); | |
| if (!info) { | |
| ret = -1; | |
| goto cleanup_png; | |
| } | |
| if (setjmp(png_jmpbuf(png))) { | |
| ret = -1; | |
| goto cleanup_png; | |
| } | |
| png_init_io(png, stdin); | |
| png_read_info(png, info); | |
| int width = png_get_image_width(png, info); | |
| int height = png_get_image_height(png, info); | |
| int color_type = png_get_color_type(png, info); | |
| int bit_depth = png_get_bit_depth(png, info); | |
| if (bit_depth == 16) { | |
| png_set_strip_16(png); | |
| } | |
| if (color_type == PNG_COLOR_TYPE_PALETTE) { | |
| png_set_palette_to_rgb(png); | |
| } | |
| if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { | |
| png_set_expand_gray_1_2_4_to_8(png); | |
| } | |
| if(png_get_valid(png, info, PNG_INFO_tRNS)) { | |
| png_set_tRNS_to_alpha(png); | |
| } | |
| if(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) { | |
| png_set_filler(png, 0xFF, PNG_FILLER_AFTER); | |
| } | |
| if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { | |
| png_set_gray_to_rgb(png); | |
| } | |
| png_read_update_info(png, info); | |
| png_bytep row = malloc(png_get_rowbytes(png, info)); | |
| if (!row) { | |
| ret = -1; | |
| goto cleanup_png; | |
| } | |
| png_read_row(png, row, NULL); | |
| size_t siz = (width / height) * 4 - sizeof(header); | |
| if (siz % 4 != 0) { | |
| ret = -1; | |
| goto cleanup_png_cols; | |
| } | |
| uint8_t *data = malloc(siz); | |
| if (!data) { | |
| ret = -1; | |
| goto cleanup_png_cols; | |
| } | |
| png_byte *p = row + height * sizeof(header); | |
| for (size_t i = 0; i < siz/4; i++) { | |
| data[i*4+0] = *(p+i*height*4+3); | |
| data[i*4+1] = *(p+i*height*4+0); | |
| data[i*4+2] = *(p+i*height*4+1); | |
| data[i*4+3] = *(p+i*height*4+2); | |
| } | |
| size_t off = 0; | |
| for (size_t i = 0; i < (siz / buf) + ((siz%buf) && 1); i++) { | |
| size_t exp = buf; | |
| if (off+exp > siz) { | |
| exp = siz - off; | |
| } | |
| if (fwrite(data+off, exp, 1, stdout) != 1) { | |
| perror("writing stdout"); | |
| ret = -1; | |
| goto cleanup_data; | |
| } | |
| off += exp; | |
| } | |
| cleanup_data: | |
| free(data); | |
| cleanup_png_cols: | |
| png_free(png, row); | |
| cleanup_png: | |
| png_destroy_read_struct(&png, &info, NULL); | |
| return ret; | |
| } | |
| void print_help(char *name) { | |
| printf( | |
| "USAGE: <data stdout> | %s [-e|-d] [-s scale] > file\n" | |
| " -e encodes raw data to png\n" | |
| " -d decodes png to raw data\n" | |
| " -s sets image upscale factor\n" | |
| , name); | |
| } | |
| typedef enum { | |
| MODE_UNKNOWN = 0, | |
| MODE_ENCODE = 1<<0, | |
| MODE_DECODE = 1<<1, | |
| } png_mode_t; | |
| int main(int argc, char **argv) { | |
| png_mode_t mode = MODE_UNKNOWN; | |
| int opt; | |
| int scale = 10; | |
| char *endptr = NULL; | |
| while ((opt = getopt(argc, argv, "eds:")) != -1) { | |
| switch (opt) { | |
| case 'e': | |
| mode |= MODE_ENCODE; | |
| break; | |
| case 'd': | |
| mode |= MODE_DECODE; | |
| break; | |
| case 's': | |
| scale = strtol(optarg, &endptr, 10); | |
| if (*endptr != '\0') { | |
| printf("invalid argument to -s: %s\n", optarg); | |
| print_help(argv[0]); | |
| return -1; | |
| } | |
| break; | |
| } | |
| } | |
| if (scale <= 0) { | |
| printf("invalid argument to -s: %d\n", scale); | |
| print_help(argv[0]); | |
| return 1; | |
| } | |
| #ifdef _WIN32 | |
| freopen(NULL, "r+b", stdin); | |
| _setmode(_fileno(stdin), _O_BINARY); | |
| freopen(NULL, "w+b", stdout); | |
| _setmode(_fileno(stdout), _O_BINARY); | |
| #endif | |
| switch (mode) { | |
| case MODE_ENCODE: | |
| return mode_encode(scale); | |
| case MODE_DECODE: | |
| return mode_decode(); | |
| default: | |
| print_help(argv[0]); | |
| return 1; | |
| } | |
| return 0; | |
| } |