~ajpaon/image-to-ansi

fff8eb732adbddc32f25cdf9a78343b5249424a1 — Andrew Paon 1 year, 5 months ago
bmp-to-ansi initial commit
2 files changed, 155 insertions(+), 0 deletions(-)

A main.c
A makefile
A  => main.c +143 -0
@@ 1,143 @@
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>

static uint8_t *MapFile(const char *filename) {
  int fd = open(filename, O_RDONLY);
  if (fd == -1) {
    fprintf(stderr, "Failed to open: %s\n", filename);
    return NULL;
  }

  struct stat sb;
  if (fstat(fd, &sb) == -1) {
    fprintf(stderr, "Failed to stat: %s\n", filename);
    return NULL;
  }

  uint8_t *content =
      (uint8_t *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  if (content == MAP_FAILED) {
    fprintf(stderr, "Failed to map: %s\n", filename);
    return NULL;
  }

  return content;
}

static void Usage() {
  fprintf(stderr, "Usage:\n");
  fprintf(stderr, "  ./bmp-to-ansi <bmp file>\n");
}

struct BMPMeta {
  int32_t width, height;
  uint8_t *pixels;
};

static void bmp_meta_print(struct BMPMeta bmp) {
  printf("%dx%d\n", bmp.width, bmp.height);
  printf("pixels at %p\n", bmp.pixels);
}

enum BMP_PARSE_ERROR {
  BMP_PARSE_SUCCESS,
  BMP_PARSE_INVALID_FORMAT,
  BMP_PARSE_NOT_24_BIT,
  BMP_PARSE_COMPRESSION,
};

static void bmp_parse_error_display(enum BMP_PARSE_ERROR err) {
  assert(err != BMP_PARSE_SUCCESS);

  switch (err) {
  case BMP_PARSE_SUCCESS:
    assert(0);
    break;
  case BMP_PARSE_INVALID_FORMAT:
    fprintf(stderr, "ERROR: Parser only supports Windows-style BMP\n");
    break;
  case BMP_PARSE_NOT_24_BIT:
    fprintf(stderr, "ERROR: Parser only supports 24-bit color bitmaps\n");
    break;
  case BMP_PARSE_COMPRESSION:
    fprintf(stderr, "ERROR: Parser does not support compressed bitmaps\n");
    break;
  }
}

static enum BMP_PARSE_ERROR parse_bmp(struct BMPMeta *ret, uint8_t *content) {
  if (content[0] != 'B' || content[1] != 'M') {
    return BMP_PARSE_INVALID_FORMAT;
  }

  int32_t pixel_offset;
  memcpy(&pixel_offset, content + 0x0a, 4);
  ret->pixels = content + pixel_offset;

  memcpy(&ret->width, content + 0x12, 4);
  memcpy(&ret->height, content + 0x16, 4);

  int16_t bits_per_pixel;
  memcpy(&bits_per_pixel, content + 0x1c, 2);

  if (bits_per_pixel != 24) {
    return BMP_PARSE_NOT_24_BIT;
  }

  int32_t compression;
  memcpy(&compression, content + 0x1e, 4);

  if (compression != 0) {
    return BMP_PARSE_COMPRESSION;
  }

  return BMP_PARSE_SUCCESS;
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    Usage();
    return EXIT_FAILURE;
  }

  uint8_t *content = (uint8_t *)MapFile(argv[1]);
  if (content == NULL) {
    return EXIT_FAILURE;
  }

  struct BMPMeta bmp;
  int err = parse_bmp(&bmp, content);

  if (err != BMP_PARSE_SUCCESS) {
    bmp_parse_error_display(err);
    return EXIT_FAILURE;
  }

  const int bytes_per_pixel = 3;

  int bytes_per_row = bytes_per_pixel * bmp.width;
  int pixel_buffer_size = bytes_per_row * bmp.height;

  /* BMP pixel rows are stored bottom-up, left-to-right. Start on the top row --
   * the last one in the buffer -- and work backwards. */
  uint8_t *p = bmp.pixels + pixel_buffer_size - bytes_per_row - bytes_per_pixel;

  for (int y = 0; y < bmp.height; ++y) {
    for (int x = 0; x < bmp.width; ++x) {
      p += bytes_per_pixel;
      printf("\033[48;2;(%u);(%u);(%u) m ", p[2], p[1], p[0]);
      printf("\033[48;2;(%u);(%u);(%u) m ", p[2], p[1], p[0]);
    }

    p -= 2 * bytes_per_row;
    printf("\033[0m\n");
  }

  return 0;
}

A  => makefile +12 -0
@@ 1,12 @@
CC=clang
CFLAGS=-Wall -g

all: bmp-to-ansi

bmp-to-ansi: main.o
	$(CC) $(CFLAGS) $< -o $@

clean:
	rm -f *.o bmp-to-ansi

.PHONY: all clean