CFLAGS="-lpng" make pngline
Windows binary:
CFLAGS="-lpng" make pngline
Windows 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; | |
} |