Skip to content

Instantly share code, notes, and snippets.

@CQCumbers
Last active November 7, 2020 01:36
Show Gist options
  • Save CQCumbers/708ac8b55c7441ff18867468cfa26d59 to your computer and use it in GitHub Desktop.
Save CQCumbers/708ac8b55c7441ff18867468cfa26d59 to your computer and use it in GitHub Desktop.
print pixel buffers to stdout using ANSI escapes
#ifndef IMGPRINT_H
#define IMGPRINT_H
#include <stdio.h>
// w and h are image width and height in pixels
// width must always be a whole number of bytes
// print 24bpp RGB888 image
// requires true color support
void imgprint24(const char *img, int w, int h) {
for (int y = 0; y < h - 1; y += 2) {
for (int x = 0; x < w; ++x) {
const char *hi = img + ((y + 0) * w + x) * 3;
const char *lo = img + ((y + 1) * w + x) * 3;
printf("\x1b[38;2;%hhu;%hhu;%hhum", hi[0], hi[1], hi[2]);
printf("\x1b[48;2;%hhu;%hhu;%hhum", lo[0], lo[1], lo[2]);
printf("\xe2\x96\x80");
}
printf("\x1b[0m\n");
}
}
// print 8bpp greyscale image
// requires true color support
void imgprint8(const char *img, int w, int h) {
for (int y = 0; y < h - 1; y += 2) {
for (int x = 0; x < w; ++x) {
char hi = img[(y + 0) * w + x];
char lo = img[(y + 1) * w + x];
printf("\x1b[38;2;%hhu;%hhu;%hhum", hi, hi, hi);
printf("\x1b[48;2;%hhu;%hhu;%hhum", lo, lo, lo);
printf("\xe2\x96\x80");
}
printf("\x1b[0m\n");
}
}
// helper for imgprint4
void imgprint4_pix(char hi, char lo) {
printf("\x1b[38;5;%hhum", hi & 15);
printf("\x1b[48;5;%hhum", lo & 15);
printf("\xe2\x96\x80");
}
// print 4bpp 16-color image
// uses terminal 256-color palette
void imgprint4(const char *img, int w, int h) {
for (int y = 0; y < h - 1; y += 2) {
for (int x = 0; x < w; x += 2) {
char hi = img[((y + 0) * w + x) / 2];
char lo = img[((y + 1) * w + x) / 2];
imgprint4_pix(hi >> 4, lo >> 4);
imgprint4_pix(hi & 15, lo & 15);
}
printf("\x1b[0m\n");
}
}
// helper for imgprint2
void imgprint2_pix(char hi, char lo) {
static int codes[] = { 30, 90, 37, 97 };
int chi = codes[hi & 3] + 0;
int clo = codes[lo & 3] + 10;
printf("\x1b[%d;%dm", chi, clo);
printf("\xe2\x96\x80");
}
// print 2bpp greyscale image
void imgprint2(const char *img, int w, int h) {
for (int y = 0; y < h - 1; y += 2) {
for (int x = 0; x < w; x += 4) {
char hi = img[((y + 0) * w + x) / 4];
char lo = img[((y + 1) * w + x) / 4];
for (int i = 6; i >= 0; i -= 2) {
imgprint2_pix(hi >> i, lo >> i);
}
}
printf("\x1b[0m\n");
}
}
// helper for imgprint1
void imgprint1_pix(char hi, char lo) {
int chi = (hi & 1) ? 97 : 30;
int clo = (lo & 1) ? 107 : 40;
printf("\x1b[%d;%dm", clo, chi);
printf("\xe2\x96\x80");
}
// print 1bpp black & white image
void imgprint1(const char *img, int w, int h) {
for (int y = 0; y < h - 1; y += 2) {
for (int x = 0; x < w; x += 8) {
char hi = img[((y + 0) * w + x) / 8];
char lo = img[((y + 1) * w + x) / 8];
for (int i = 7; i >= 0; --i) {
imgprint1_pix(hi >> i, lo >> i);
}
}
printf("\x1b[0m\n");
}
}
#endif
#include "imgprint.h"
#include <stdlib.h>
typedef unsigned char byte;
typedef struct {
int width, height;
int intensity;
char p, type;
} image_t;
static image_t read_ppm(FILE *fp) {
// verify image format is P6
image_t img;
fscanf(fp, "%c%c\n", &img.p, &img.type);
int p6 = (img.p == 'P' && img.type == '6');
if (!p6) printf("Not P6 PPM file\n"), exit(1);
// skip comment lines
char line[200];
while (1) {
fscanf(fp, "%c", line);
if (line[0] != '#') break;
fgets(line, 200, fp);
}
ungetc(line[0], fp);
// check image dimensions
fscanf(fp, "%d %d\n", &img.width, &img.height);
if (img.width & 7) printf("Incompatible width\n"), exit(1);
fscanf(fp, "%d\n", &img.intensity);
return img;
}
int main(int argc, char *argv[]) {
// open test image in ppm format
if (argc != 2) printf("Usage: test image.ppm\n"), exit(1);
FILE *fp = fopen(argv[1], "rb");
if (!fp) printf("Can't find file %s\n", argv[1]), exit(1);
// allocate and read into buffer
image_t img = read_ppm(fp);
int size = img.width * img.height;
byte *pix = (byte*)malloc(size * 3);
fread(pix, 1, size * 3, fp);
// test printing 24bpp color image
printf("width: %d, height: %d\n", img.width, img.height);
imgprint24(pix, img.width, img.height);
// test printing 8bpp greyscale image
for (int i = 0; i < size; ++i) {
float r = pix[i * 3 + 0] * 0.21;
float g = pix[i * 3 + 1] * 0.71;
float b = pix[i * 3 + 2] * 0.07;
pix[i] = (byte)(r + g + b);
}
imgprint8(pix, img.width, img.height);
// test printing 4bpp paletted image
for (int i = 0; i < size; i += 2) {
byte left = pix[i + 0] >> 4;
byte right = pix[i + 1] >> 4;
pix[i / 2] = (left << 4) | right;
}
imgprint4(pix, img.width, img.height);
// test printing 2bpp greyscale image
for (int i = 0; i < size / 2; ++i) {
byte left = (pix[i] >> 6) & 3;
byte right = (pix[i] >> 2) & 3;
byte val = (left << 2) | right;
if (i & 1) pix[i / 2] |= val;
else pix[i / 2] = val << 4;
}
imgprint2(pix, img.width, img.height);
// test printing 1bpp black & white image
for (int i = 0; i < size / 4; ++i) {
byte val = (pix[i] >> 4) & 8;
val |= (pix[i] >> 3) & 4;
val |= (pix[i] >> 2) & 2;
val |= (pix[i] >> 1) & 1;
if (i & 1) pix[i / 2] |= val;
else pix[i / 2] = val << 4;
}
imgprint1(pix, img.width, img.height);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment