Skip to content

Instantly share code, notes, and snippets.

@iamtakingiteasy
Last active August 4, 2019 01:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamtakingiteasy/85d44d7b92b6831045fde1e05111c732 to your computer and use it in GitHub Desktop.
Save iamtakingiteasy/85d44d7b92b6831045fde1e05111c732 to your computer and use it in GitHub Desktop.
pngline
#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;
}
echo "hirefuse gumin-domo!" | ./pngline -s 10 -e  | ./pngline -d
echo "hirefuse gumin-domo!" | ./pngline -s 10 -e  > msg.png
./pngline -d < msg.png
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment