aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bitmap.c160
-rw-r--r--src/bitmap.h70
-rw-r--r--src/character.c10
-rw-r--r--src/character.h12
-rw-r--r--src/color.c74
-rw-r--r--src/color.h33
-rw-r--r--src/m.c22
-rw-r--r--src/m.h14
-rw-r--r--src/main.c235
9 files changed, 630 insertions, 0 deletions
diff --git a/src/bitmap.c b/src/bitmap.c
new file mode 100644
index 0000000..bb88b46
--- /dev/null
+++ b/src/bitmap.c
@@ -0,0 +1,160 @@
+#include "bitmap.h"
+
+uint32_t bitmap_flip_byte(unsigned char* _v, int _c)
+{
+ uint32_t ret = 0;
+ uint32_t counter = (_c-1) * 8;
+
+ for(int i = 0; i < _c; i++)
+ {
+ ret |= (uint32_t)(_v[i] << (counter));
+ counter -= 8;
+ }
+
+ return ret;
+}//flip
+
+struct bitmap_pixel_data bitmap_read(char *_file)
+{
+ struct bitmap_pixel_data ret;
+ struct bitmap_file_header header;
+
+ ret.R = ret.G = ret.B = NULL;
+ ret.error = 1;
+
+ FILE *bitmap = fopen(_file,"rb");
+
+ if(!bitmap)
+ return ret;
+
+ header = bitmap_read_file_header(bitmap);
+
+ if(header.error)
+ return ret;
+
+ if(header.biBitCount != 24)
+ return ret;
+
+ if(header.biCompression != 0)
+ return ret;
+
+ ret = bitmap_read_pixel_data(bitmap, header);
+
+
+ free(header.tables);
+ fclose(bitmap);
+
+ ret.error = 0;
+ return ret;
+}
+
+struct bitmap_file_header bitmap_read_file_header(FILE *_file)
+{
+ struct bitmap_file_header ret;
+ unsigned char fileheader[_HEADER_SIZE];
+ uint32_t read_counter = 0;
+
+ ret.error = 1;
+
+ size_t tt = fread((void*)&fileheader, sizeof(char), _HEADER_SIZE, _file);
+ read_counter += _HEADER_SIZE;
+
+ if(!tt)
+ return ret;
+
+ //Copy file header
+ ret.bfType = (uint16_t) bitmap_flip_byte(&fileheader[BF_TYPE], sizeof(ret.bfType));
+
+ if(ret.bfType != (uint16_t)IDENTIFIER)
+ return ret;
+
+ ret.bfSize = (uint32_t) bitmap_flip_byte(&fileheader[BF_SIZE], sizeof(ret.bfSize));
+ ret.bfOffBits = *(uint32_t*) &fileheader[BF_OFF_BITS];
+ ret.biSize = *(uint32_t*) &fileheader[BI_SIZE];
+ ret.biWidth = *(int32_t*) &fileheader[BI_WIDTH];
+ ret.biHeight = *(int32_t*) &fileheader[BI_HEIGHT];
+ ret.biBitCount = *(uint16_t*) &fileheader[BI_BIT_COUNT];
+ ret.biCompression = (uint32_t) bitmap_flip_byte(&fileheader[BI_COMPRESSION], sizeof(ret.biCompression));
+ ret.biSizeImage = *(uint32_t*) &fileheader[BI_SIZE_IMAGE];
+ ret.biClrUsed = (uint32_t) bitmap_flip_byte(&fileheader[BI_CLR_USED], sizeof(ret.biClrUsed));
+ ret.biClrImportant = (uint32_t) bitmap_flip_byte(&fileheader[BI_CLR_IMPORTANT], sizeof(ret.biClrImportant));
+
+
+ //Read to start of Pixel block
+ //This block contains Colormasks and Colortables.
+ ret.tablesc = ret.bfOffBits - read_counter;
+ ret.tables = malloc(sizeof(char)* ret.tablesc);
+ fread(ret.tables, sizeof(char), ret.tablesc, _file);
+ //////////
+
+ ret.error = 0;
+ return ret;
+}
+
+struct bitmap_pixel_data bitmap_read_pixel_data(FILE *_file, struct bitmap_file_header _header)
+{
+ uint32_t **bitmap_buff;
+
+ struct bitmap_pixel_data ret;
+
+ uint32_t row_size = _header.biWidth * 3;
+ while(row_size%4)
+ row_size++;
+
+ ret.x = _header.biWidth;
+ ret.y = _header.biHeight < 0 ? -_header.biHeight: _header.biHeight;
+
+ //If biHeight > 0 Data starts with last row!!
+
+ //Allocate 2D array
+ //!!
+ //bitmap_buff indeces are flipped!! [y][x]!!!!!
+ bitmap_buff = malloc(sizeof(*bitmap_buff) * _header.biHeight);
+ for(int i = 0; i < ret.y; i++)
+ {
+ bitmap_buff[i] = malloc(sizeof(*bitmap_buff[i]) * _header.biWidth);
+ }
+
+ //Copy Bitmap into bitmap_buff
+ for(int row = 0; row < _header.biHeight; row++)
+ {
+ //printf("Row %i\n", row);
+ //fread(bitmap_buff[row], sizeof(char), row_size, bitmap);
+ for(int col = 0; col < _header.biWidth; col++)
+ fread(&bitmap_buff[row][col], 1, 3, _file);
+
+ for(int i = 0; i < row_size - (_header.biWidth * 3); i++) //read excess NULL-Bytes
+ fgetc(_file);
+ }
+
+ ret.x = _header.biWidth;
+ ret.y = _header.biHeight < 0 ? -_header.biHeight: _header.biHeight;
+
+ ret.R = malloc(sizeof(*ret.R) * ret.x);
+ ret.G = malloc(sizeof(*ret.G) * ret.x);
+ ret.B = malloc(sizeof(*ret.B) * ret.x);
+ for(int i = 0; i < ret.x; i++)
+ {
+ ret.R[i] = malloc(sizeof(*ret.R[i]) * ret.y);
+ ret.G[i] = malloc(sizeof(*ret.G[i]) * ret.y);
+ ret.B[i] = malloc(sizeof(*ret.B[i]) * ret.y);
+ }
+
+ for(int y = 0; y < ret.y; y++)
+ {
+ for(int x = 0; x < ret.x; x++)
+ {
+ int row = _header.biHeight > 0 ? (ret.y - 1) - y : y;
+
+ ret.R[x][y] = (bitmap_buff[row][x] & 0xff0000)>>16;
+ ret.G[x][y] = (bitmap_buff[row][x] & 0x00ff00)>>8;
+ ret.B[x][y] = (bitmap_buff[row][x] & 0x0000ff);
+ }
+ }
+
+ for(int i = 0; i < ret.y; i++)
+ free(bitmap_buff[i]);
+ free(bitmap_buff);
+
+ return ret;
+}
diff --git a/src/bitmap.h b/src/bitmap.h
new file mode 100644
index 0000000..d222ffb
--- /dev/null
+++ b/src/bitmap.h
@@ -0,0 +1,70 @@
+#ifndef _BITMAP_H_
+#define _BITMAP_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define _HEADER_SIZE 54 //Fileheader + infoheader
+#define IDENTIFIER 0x424d //BM BitMap identifier
+
+//Address Definitions
+#define BF_TYPE 0x00
+#define BF_SIZE 0x02
+#define BF_OFF_BITS 0x0a
+
+#define BI_SIZE 0x0e
+#define BI_WIDTH 0x12
+#define BI_HEIGHT 0x16
+#define BI_BIT_COUNT 0x1c
+#define BI_COMPRESSION 0x1e
+#define BI_SIZE_IMAGE 0x22
+#define BI_CLR_USED 0x2e
+#define BI_CLR_IMPORTANT 0x32
+
+#define R(x) (0xff0000 & x) >> 16
+#define G(x) (0x00ff00 & x) >> 8
+#define B(x) (0x0000ff & x)
+
+struct bitmap_file_header
+{
+ uint8_t error;
+
+ uint16_t bfType;
+ uint32_t bfSize;
+ uint32_t bfOffBits;
+
+ uint32_t biSize;
+ int32_t biWidth;
+ int32_t biHeight;
+ uint16_t biBitCount;
+ uint32_t biCompression;
+ uint32_t biSizeImage;
+ uint32_t biClrUsed;
+ uint32_t biClrImportant;
+
+ unsigned char *tables;
+ uint32_t tablesc;
+};
+
+struct bitmap_pixel_data
+{
+ unsigned int x,y;
+ uint8_t **R;
+ uint8_t **G;
+ uint8_t **B;
+
+ uint8_t error;
+};
+
+
+uint32_t bitmap_flip_byte(unsigned char* _v, int _c);
+
+struct bitmap_pixel_data bitmap_read(char *_file);
+
+struct bitmap_file_header bitmap_read_file_header(FILE *_file);
+
+struct bitmap_pixel_data bitmap_read_pixel_data(FILE *_file, struct bitmap_file_header _header);
+
+
+#endif /* end of include guard: _BITMAP_H_ */
diff --git a/src/character.c b/src/character.c
new file mode 100644
index 0000000..658f0be
--- /dev/null
+++ b/src/character.c
@@ -0,0 +1,10 @@
+#include "character.h"
+
+const char character_luminance_map [] = {' ', ' ', '.', ',', '`', '-', '~', '"', '*', ':', ';', '<', '!', '/', '?', '%', '&', '=', '$', '#'};
+//const char map[] = {' ', '`', '.', ',', ':', ';', '\"', '+', '#', '@'};
+
+char calc_char(uint8_t _c , uint8_t _min, uint8_t _max)
+{
+ float c = (float)(_c) / (_max - _min);
+ return character_luminance_map [(int)((sizeof(character_luminance_map)-1) * (c))];
+}
diff --git a/src/character.h b/src/character.h
new file mode 100644
index 0000000..7e17cd7
--- /dev/null
+++ b/src/character.h
@@ -0,0 +1,12 @@
+#ifndef _CHARACTER_H_
+#define _CHARACTER_H_
+
+#include <stdint.h>
+
+//Both maps produce very different results
+const char character_luminance_map [] ;
+
+//Select Char based on 1B brightness Value
+char calc_char(uint8_t _c, uint8_t _min, uint8_t _max);
+
+#endif //_CHARACTER_H_
diff --git a/src/color.c b/src/color.c
new file mode 100644
index 0000000..d822d56
--- /dev/null
+++ b/src/color.c
@@ -0,0 +1,74 @@
+#include "color.h"
+
+struct console_color colors[] = { //Standard VGA colors
+ {0, 0, 0, "30"}, //Black
+ {170, 0, 0, "31"}, //red
+ {0, 170, 0, "32"}, //Green
+ {170, 85, 0, "33"}, //Brown
+ {0, 0, 170, "34"}, //blue
+ {170, 0, 170, "35"}, //Magenta
+ {0, 170, 170, "36"}, //Cyan
+ {170,170,170, "37"}, //Grey
+ {85,85,85, "30;1"},
+ {255,85,85, "31;1"},
+ {85,255,85, "32;1"},
+ {255,255,85, "33;1"},
+ {85,85,255, "34;1"},
+ {255,85,255, "35;1"},
+ {85,255,255, "36;1"},
+ {255, 255, 255, "37;1"}
+};
+
+uint8_t rgb_avg(uint8_t R, uint8_t G, uint8_t B)
+{
+ uint8_t ret;
+
+ ret = sqrt( 0.299*pow(R,2) + 0.587*pow(G,2) + 0.114*pow(B,2) ); //(char)(R+R+B+G+G+G)/6;
+
+ return ret;
+}
+
+char* calc_col(uint8_t R, uint8_t G, uint8_t B)
+{
+ unsigned int nearest_num = 0;
+ uint8_t a2[3];
+ a2[0] = colors[0].R;
+ a2[1] = colors[0].G;
+ a2[2] = colors[0].B;
+
+ uint8_t a1[] = {R,G,B};
+
+ //Normalize the color
+ //Does not really work all that well
+ /*while(a1[0] < 255 && a1[1] < 255 && a1[2] < 255)
+ {
+ a1[0]++;
+ a1[1]++;
+ a1[2]++;
+ }*/
+
+ float nearest_val = distance( a1, a2 );
+
+ for( unsigned int i = 1; i < _COLORS_SIZE; i++)
+ {
+ a2[0] = colors[i].R;
+ a2[1] = colors[i].G;
+ a2[2] = colors[i].B;
+
+ float dist = distance(a1, a2);
+ if(dist < nearest_val){
+ nearest_num = i;
+ nearest_val = dist;
+ }
+ }
+ return colors[ nearest_num ].no;
+}
+char* calc_col_ansi(uint8_t R, uint8_t G, uint8_t B)
+{
+ int num = 36 * (R/51) + 6 * (G/51) + (B/51);
+ char *c = malloc(9);
+ snprintf( c, 6, "38;5;" );
+ snprintf( c + 5, 4, "%i", num + 16 );
+
+ return c;
+}
diff --git a/src/color.h b/src/color.h
new file mode 100644
index 0000000..10d376e
--- /dev/null
+++ b/src/color.h
@@ -0,0 +1,33 @@
+#ifndef _COLOR_H_
+#define _COLOR_H_
+
+#include <stdint.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "m.h"
+
+#define _COLORS_SIZE 16u
+
+struct console_color
+{
+ uint8_t R;
+ uint8_t G;
+ uint8_t B;
+
+ char *no;
+};
+
+struct console_color colors[ _COLORS_SIZE ];
+
+//Calculate luminance
+//Order LSB first: BGR
+uint8_t rgb_avg(uint8_t R, uint8_t G, uint8_t B);
+
+//Get nearest printable color in console
+char *calc_col(uint8_t R, uint8_t G, uint8_t B);
+
+char *calc_col_ansi(uint8_t R, uint8_t G, uint8_t B);
+
+#endif //_COLOR_H_
diff --git a/src/m.c b/src/m.c
new file mode 100644
index 0000000..b1401f1
--- /dev/null
+++ b/src/m.c
@@ -0,0 +1,22 @@
+#include "m.h"
+
+uint8_t avg(int argc, uint8_t *argv)
+{
+ uint8_t ret = 0;
+ uint64_t sum = 0;
+
+ for(int i = 0; i < argc; i++)
+ sum += (uint64_t)argv[i];
+
+ ret = (char)(sum / argc);
+
+ return ret;
+}//avg
+
+
+float distance(uint8_t _1[3], uint8_t _2[3])
+{
+ return fabs( sqrt( pow((double)_2[0] - (double)_1[0], 2) +
+ pow((double)_2[1] - (double)_1[1], 2) +
+ pow((double)_2[2] - (double)_1[2], 2) ) );
+}
diff --git a/src/m.h b/src/m.h
new file mode 100644
index 0000000..25aff9e
--- /dev/null
+++ b/src/m.h
@@ -0,0 +1,14 @@
+#ifndef _M_H_
+#define _M_H_
+
+#include <stdint.h>
+#include <math.h>
+
+
+//Calculate average
+uint8_t avg(int argc, uint8_t *argv);
+
+//Distance of 2 3d vectors
+float distance(uint8_t _1[3], uint8_t _2[3]);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b8bf074
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,235 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <string.h>
+
+#include "bitmap.h"
+#include "character.h"
+#include "m.h"
+#include "color.h"
+
+#ifdef _DEBUG
+#warning "Compiling with DEBUG"
+#define DEBUG_PRINTF(...) { printf(__VA_ARGS__); }
+#else
+#define DEBUG_PRINTF(...) { }
+#endif
+
+#define CHAR_SIZE_X 2 //How many pixels should form one ASCII char?
+#define CHAR_SIZE_Y (2 * CHAR_SIZE_X)
+
+struct prog_param
+{
+ char *filename;
+ unsigned int charsize_x;
+ unsigned int charsize_y;
+ uint8_t color;
+};
+
+struct prog_param parse_args(int argc, char *argv[]);
+
+void print_help( void );
+
+int main(int argc, char *argv[])
+{
+ struct prog_param args = parse_args(argc, argv);
+
+ //Stores a luminance array
+ uint8_t **ascii_buff;
+ //Stores a color array
+ char* **col_buff;
+
+ uint8_t b_max = 0x00;
+ uint8_t b_min = 0xff;
+
+ struct bitmap_pixel_data bitmap;
+
+ bitmap = bitmap_read(args.filename);
+
+ if(bitmap.error) {
+ printf("Error reading file\n");
+ return 1;
+ }
+
+ //x and y size of ASCII-image
+ unsigned int size_x,size_y;
+ size_x = bitmap.x / args.charsize_x;
+ size_y = bitmap.y / args.charsize_y;
+
+ DEBUG_PRINTF("Output size: %u x %u\n", size_x, size_y);
+
+ //Where the chars are stored
+ ascii_buff = malloc(sizeof(*ascii_buff) * size_x);
+ for (int i = 0; i < size_x; i++)
+ ascii_buff[i] = malloc(sizeof(ascii_buff[i]) * size_y);
+
+ //Where the color is stored, if activated
+ if(args.color) {
+ col_buff = malloc(sizeof(*col_buff) * size_x);
+ for (int i = 0; i < size_x; i++)
+ col_buff[i] = malloc(sizeof(col_buff[i]) * size_y);
+ }
+
+ //Nest thine Lööps
+ //Very not optimal Variable names!!!!!!!!!!!!!
+ //
+ //For every size_x * size_y block: calculate average values of pixel blocks
+ for(unsigned int x = 0; x < size_x; x++) {
+ for(unsigned int y = 0; y < size_y; y++) {
+ uint8_t brightness [ args.charsize_x ][ args.charsize_y ]; //Average brightness of every pixel
+ uint8_t cc[ 3 ][ args.charsize_x * args.charsize_y ]; //RGB Values of Pixels
+ unsigned int cc_counter = 0;
+
+ //Iterate through Pixel block
+ for(unsigned int row_c = 0; row_c < args.charsize_y; row_c++) {
+ unsigned int row = y * args.charsize_y + row_c; //Actual position in Bitmap
+
+ for(unsigned int col_c = 0; col_c < args.charsize_x; col_c++) {
+ unsigned int col = x * args.charsize_x + col_c; //Actual position in bitmap
+
+ brightness[col_c][row_c] = rgb_avg(
+ bitmap.R[col][row],
+ bitmap.G[col][row],
+ bitmap.B[col][row]);
+
+ if(args.color) {
+ cc[0][cc_counter] = bitmap.R[col][row];
+ cc[1][cc_counter] = bitmap.G[col][row];
+ cc[2][cc_counter] = bitmap.B[col][row];
+ cc_counter++;
+ }//if
+ }//for col_c
+ }//for row_c
+
+ ascii_buff[x][y] = avg(args.charsize_x * args.charsize_y, *brightness);
+ if(args.color == 1) {
+ col_buff[x][y] = calc_col(
+ (uint8_t)avg(args.charsize_x * args.charsize_y, cc[0]),
+ (uint8_t)avg(args.charsize_x * args.charsize_y, cc[1]),
+ (uint8_t)avg(args.charsize_x * args.charsize_y, cc[2]));
+ } else if(args.color == 2) {
+ col_buff[x][y] = calc_col_ansi(
+ (uint8_t)avg(args.charsize_x * args.charsize_y, cc[0]),
+ (uint8_t)avg(args.charsize_x * args.charsize_y, cc[1]),
+ (uint8_t)avg(args.charsize_x * args.charsize_y, cc[2]));
+ }
+
+ if((uint8_t)ascii_buff[x][y] < b_min)
+ b_min = ascii_buff[x][y];
+ if((uint8_t)ascii_buff[x][y] > b_max)
+ b_max = ascii_buff[x][y];
+ }//for y
+ }//for x
+
+ DEBUG_PRINTF("Brightness Values: Min: %u Max: %u\n", b_min, b_max);
+
+ if(args.color)
+ printf("\e[0m");//Default colors
+
+ //Print buffer
+ for(int y = 0; y<size_y; y++) {
+ for(int x = 0; x < size_x; x++) {
+ if(args.color)
+ printf("\e[%sm", col_buff[x][y]);
+
+ printf("%c", calc_char(ascii_buff[x][y], 0,255));//b_min, b_max));
+ }
+ printf("\n");
+ }
+
+ if(args.color)
+ printf("\e[0m");//Default colors
+
+ DEBUG_PRINTF("Finished!\n");
+
+ //Cleanup
+ for(int i = 0; i < size_x; i++)
+ free (ascii_buff[i]);
+ free(ascii_buff);
+
+ for(int i = 0; i < bitmap.x; i++) {
+ free(bitmap.R[i]);
+ free(bitmap.G[i]);
+ free(bitmap.B[i]);
+ }
+ free(bitmap.R);
+ free(bitmap.G);
+ free(bitmap.B);
+
+ return 0;
+}//main
+
+struct prog_param parse_args(int argc, char *argv[])
+{
+ struct prog_param ret;
+
+ ret.filename = NULL;
+ ret.charsize_x = CHAR_SIZE_X;
+ ret.charsize_y = 0;
+ ret.color = 0;
+
+ for (int i = 1; i < argc; i++) {
+ if(argv[i][0] == '-') {
+ int icpy = i;
+ for(int o = 1; o < strlen(argv[icpy]); o++) {
+ switch(argv[icpy][o]) {
+ case 'h':
+ print_help();
+ exit(1);
+ break;
+ case 'x':
+ DEBUG_PRINTF("x\n");
+ i++;
+ ret.charsize_x = atoi(argv[i]);
+ break;
+ case 'y':
+ DEBUG_PRINTF("y\n");
+ i++;
+ ret.charsize_y = atoi(argv[i]);
+ break;
+ case 'c':
+ ret.color = 1;
+ break;
+ case 'C':
+ ret.color = 2;
+ break;
+ default:
+ printf("Unrecognized Option\n");
+ print_help();
+ exit(1);
+ };//switch
+ }//for o
+ }//if
+ else if(ret.filename == NULL) {
+ ret.filename = argv[i];
+ } else {
+ printf("Wrong number of arguments\n");
+ print_help();
+ exit(1);
+ }
+ }//for i
+
+ if(ret.filename == NULL)
+ {
+ printf("No input file.\n");
+ print_help();
+ exit(1);
+ }
+
+ if(!ret.charsize_y)
+ ret.charsize_y = 2 * ret.charsize_x;
+
+ return ret;
+}
+
+void print_help( void )
+{
+ printf("ASCIIMap\n(c) 2019 Jonas Gunz, github.com/kompetenzbolzen/AsciiMap\n");
+ printf("ASCIIMap prints a ASCII representation of a bitmap image\n\nUsage: [OPTIONS] FILENAME\n");
+ printf("Options:\n -h: Print this help message\n -x VAL: set the width of block wich makes up one character. Default: %i\n", CHAR_SIZE_X);
+ printf(" -y VAL: set the height of block wich makes up one character. Default: 2*x\n -c: Print in 7 color mode. Default: OFF\n");
+ printf(" -C: Print in ANSI 256 color mode. Default: OFF\n");
+}
+
+