-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
754338f
commit 46764a1
Showing
5 changed files
with
230 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
cc_library( | ||
visibility = ["//visibility:public"], | ||
name = "ascii", | ||
hdrs = glob(["*.hh"]), | ||
srcs = glob(["*.cc"]), | ||
includes = [""], | ||
deps = [ | ||
"//thirdparty/nanosvg", | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
#include "ascii.hh" | ||
#include <stdio.h> | ||
#include <math.h> | ||
#define NANOSVG_IMPLEMENTATION // Expands implementation | ||
#define NANOSVG_CPLUSPLUS | ||
#define NANOSVGRAST_CPLUSPLUS | ||
#define NANOSVGRAST_IMPLEMENTATION | ||
#include "nanosvg.h" | ||
#include "nanosvgrast.h" | ||
|
||
// Convert RGB24 to xterm-256 8-bit value | ||
// For simplicity, assume RGB space is perceptually uniform. | ||
// There are 5 places where one of two outputs needs to be chosen when the | ||
// input is the exact middle: | ||
// - The r/g/b channels and the gray value: the higher value output is chosen. | ||
// - If the gray and color have same distance from the input - color is chosen. | ||
static unsigned char rgb_to_x256(uint8_t r, uint8_t g, uint8_t b) | ||
{ | ||
// Calculate the nearest 0-based color index at 16 .. 231 | ||
# define v2ci(v) (v < 48 ? 0 : v < 115 ? 1 : (v - 35) / 40) | ||
int ir = v2ci(r), ig = v2ci(g), ib = v2ci(b); // 0..5 each | ||
# define color_index() (36 * ir + 6 * ig + ib) /* 0..215, lazy evaluation */ | ||
|
||
// Calculate the nearest 0-based gray index at 232 .. 255 | ||
int average = (r + g + b) / 3; | ||
int gray_index = average > 238 ? 23 : (average - 3) / 10; // 0..23 | ||
|
||
// Calculate the represented colors back from the index | ||
static const int i2cv[6] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff}; | ||
int cr = i2cv[ir], cg = i2cv[ig], cb = i2cv[ib]; // r/g/b, 0..255 each | ||
int gv = 8 + 10 * gray_index; // same value for r/g/b, 0..255 | ||
|
||
// Return the one which is nearer to the original input rgb value | ||
# define dist_square(A,B,C, a,b,c) ((A-a)*(A-a) + (B-b)*(B-b) + (C-c)*(C-c)) | ||
int color_err = dist_square(cr, cg, cb, r, g, b); | ||
int gray_err = dist_square(gv, gv, gv, r, g, b); | ||
return color_err <= gray_err ? 16 + color_index() : 232 + gray_index; | ||
} | ||
|
||
void calcWeit(const unsigned char* rgbaImage, unsigned long widthStride, Rect rect, unsigned char& r, unsigned char& g, unsigned char& b) { | ||
unsigned long long sr = 0; | ||
unsigned long long sg = 0; | ||
unsigned long long sb = 0; | ||
const unsigned char* pixel; | ||
for(unsigned long x = rect.x; x < rect.x+rect.w; ++x) { | ||
for(unsigned long y = rect.y; y < rect.y+rect.h; ++y) { | ||
pixel = rgbaImage + (x * 4 + y * widthStride); | ||
sr += *(pixel++); | ||
sg += *(pixel++); | ||
sb += *(pixel++); | ||
} | ||
} | ||
sr /= rect.w * rect.h; | ||
sg /= rect.w * rect.h; | ||
sb /= rect.w * rect.h; | ||
r = (unsigned char)sr; | ||
g = (unsigned char)sg; | ||
b = (unsigned char)sb; | ||
} | ||
|
||
void AsciiImage::render(const unsigned char* img, unsigned long width, unsigned long height) { | ||
const std::string ASCII = " @B#Q80Rg&D$OEN9bd6MHWA%GpPSq5UZ4K3hkfmCFaVeoIs2jtJwzynu1YclT7}{Lxiv[]/\\()|?*r^<>+;~!=\",_-' "; | ||
unsigned long w = width; | ||
unsigned long h = height/2; | ||
unsigned long rw = width / w; | ||
unsigned long rh = height / h; | ||
unsigned char r, g, b; | ||
unsigned char color; | ||
unsigned int ind; | ||
Rect rect; | ||
float weit; | ||
unsigned long position; | ||
for(unsigned long y = 0; y < h; ++y) { | ||
if ((y+1)*rh > height) { | ||
break; | ||
} | ||
for(unsigned long x = 0; x < w; ++x) { | ||
if ((x+1)*rw > width) { | ||
break; | ||
} | ||
position = x + y * w; | ||
rect.x = x * rw; | ||
rect.y = y * rh; | ||
rect.w = rw; | ||
rect.h = rh; | ||
|
||
calcWeit(img, width * 4, rect, r, g, b); | ||
|
||
weit = ((float)(r + g + b)) / 3.0f / 255.0f; | ||
|
||
weit = 1-weit; | ||
weit = weit * weit; | ||
weit = 1-weit; | ||
color = rgb_to_x256(r, g, b); | ||
ind = weit * (ASCII.size() - 1); | ||
colors[position] = color; | ||
text[position] = ASCII[ind]; | ||
//printf("\x1B[38;5;%im%c\x1B[0m", color, ASCII[ind]); | ||
} | ||
//printf("\n"); | ||
} | ||
//fflush(stdout); | ||
} | ||
|
||
void AsciiImage::load() { | ||
struct NSVGimage* image; | ||
float scalex, scaley; | ||
image = nsvgParseFromFile(path.c_str(), "px", 96); | ||
if (image == NULL) { | ||
printf("Error loading image %s\n", path.c_str()); | ||
unload(); | ||
return; | ||
} | ||
|
||
if (respect_width) { | ||
width = image->width; | ||
rect.w = width; | ||
} else { | ||
width = rect.w; | ||
} | ||
|
||
scalex = ((float)width)/image->width; | ||
|
||
if (respect_height) { | ||
scaley = scalex; | ||
if (image->width == image->height) { | ||
height = width; | ||
} else { | ||
height = scaley*image->height; | ||
} | ||
rect.h = height / 2; | ||
} else { | ||
height = rect.h * 2; | ||
scaley = ((float)height)/image->height; | ||
} | ||
text = new unsigned char[rect.w * rect.h]; | ||
colors = new unsigned char[rect.w * rect.h]; | ||
// Rasterize | ||
unsigned char* img = new unsigned char[width*height*4]; | ||
NSVGrasterizer* rast = nsvgCreateRasterizer(); | ||
nsvgRasterizeStretched(rast, image, 0,0,scalex, scaley, img, width, height, width*4); | ||
nsvgDeleteRasterizer(rast); | ||
render(img, width, height); | ||
delete[] img; | ||
} | ||
|
||
void AsciiImage::print() { | ||
unsigned long position; | ||
for(unsigned long y = 0; y < rect.h; ++y) { | ||
for(unsigned long x = 0; x < rect.w; ++x) { | ||
position = x + y * rect.w; | ||
printf("\x1B[38;5;%im%c\x1B[0m", colors[position], text[position]); | ||
} | ||
printf("\n"); | ||
} | ||
fflush(stdout); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#pragma once | ||
#include <string> | ||
#include <stdlib.h> | ||
|
||
struct Rect { | ||
unsigned long x,y,w,h; | ||
}; | ||
|
||
class AsciiImage { | ||
private: | ||
bool respect_width; | ||
bool respect_height; | ||
Rect rect; | ||
unsigned long width; | ||
unsigned long height; | ||
std::string path; | ||
unsigned char* text = NULL; | ||
unsigned char* colors = NULL; | ||
|
||
void render(const unsigned char* image, unsigned long width, unsigned long height); | ||
|
||
public: | ||
AsciiImage(std::string path, Rect rect, bool respect_width, bool respect_height): respect_width(respect_width), respect_height(respect_height), rect(rect), width(rect.w), height(rect.h), path(path) { | ||
load(); | ||
} | ||
|
||
~AsciiImage() { | ||
unload(); | ||
} | ||
|
||
void load(); | ||
void print(); | ||
|
||
void unload() { | ||
if(text !=NULL){ | ||
delete[] text; | ||
} | ||
|
||
if (colors != NULL) { | ||
delete[] colors; | ||
} | ||
|
||
text = NULL; | ||
colors = NULL; | ||
} | ||
}; | ||
|
||
static unsigned char rgb_to_x256(uint8_t r, uint8_t g, uint8_t b); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,146 +1,25 @@ | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <string> | ||
#include <math.h> | ||
#define NANOSVG_IMPLEMENTATION // Expands implementation | ||
#define NANOSVG_CPLUSPLUS | ||
#define NANOSVGRAST_CPLUSPLUS | ||
#define NANOSVGRAST_IMPLEMENTATION | ||
#include "nanosvg.h" | ||
#include "nanosvgrast.h" | ||
|
||
struct Rect { | ||
unsigned long x,y,w,h; | ||
}; | ||
|
||
// Convert RGB24 to xterm-256 8-bit value | ||
// For simplicity, assume RGB space is perceptually uniform. | ||
// There are 5 places where one of two outputs needs to be chosen when the | ||
// input is the exact middle: | ||
// - The r/g/b channels and the gray value: the higher value output is chosen. | ||
// - If the gray and color have same distance from the input - color is chosen. | ||
static int rgb_to_x256(uint8_t r, uint8_t g, uint8_t b) | ||
{ | ||
// Calculate the nearest 0-based color index at 16 .. 231 | ||
# define v2ci(v) (v < 48 ? 0 : v < 115 ? 1 : (v - 35) / 40) | ||
int ir = v2ci(r), ig = v2ci(g), ib = v2ci(b); // 0..5 each | ||
# define color_index() (36 * ir + 6 * ig + ib) /* 0..215, lazy evaluation */ | ||
|
||
// Calculate the nearest 0-based gray index at 232 .. 255 | ||
int average = (r + g + b) / 3; | ||
int gray_index = average > 238 ? 23 : (average - 3) / 10; // 0..23 | ||
|
||
// Calculate the represented colors back from the index | ||
static const int i2cv[6] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff}; | ||
int cr = i2cv[ir], cg = i2cv[ig], cb = i2cv[ib]; // r/g/b, 0..255 each | ||
int gv = 8 + 10 * gray_index; // same value for r/g/b, 0..255 | ||
|
||
// Return the one which is nearer to the original input rgb value | ||
# define dist_square(A,B,C, a,b,c) ((A-a)*(A-a) + (B-b)*(B-b) + (C-c)*(C-c)) | ||
int color_err = dist_square(cr, cg, cb, r, g, b); | ||
int gray_err = dist_square(gv, gv, gv, r, g, b); | ||
return color_err <= gray_err ? 16 + color_index() : 232 + gray_index; | ||
} | ||
|
||
void calcWeit(unsigned char* rgbaImage, unsigned long widthStride, Rect rect, unsigned char& r, unsigned char& g, unsigned char& b) { | ||
unsigned long long sr = 0; | ||
unsigned long long sg = 0; | ||
unsigned long long sb = 0; | ||
unsigned char* pixel; | ||
for(unsigned long x = rect.x; x < rect.x+rect.w; ++x) { | ||
for(unsigned long y = rect.y; y < rect.y+rect.h; ++y) { | ||
pixel = rgbaImage + (x * 4 + y * widthStride); | ||
sr += *(pixel++); | ||
sg += *(pixel++); | ||
sb += *(pixel++); | ||
} | ||
} | ||
sr /= rect.w * rect.h; | ||
sg /= rect.w * rect.h; | ||
sb /= rect.w * rect.h; | ||
r = (unsigned char)sr; | ||
g = (unsigned char)sg; | ||
b = (unsigned char)sb; | ||
} | ||
|
||
void printArt(unsigned char* img, unsigned long width, unsigned long height) { | ||
const std::string ASCII = " @B#Q80Rg&D$OEN9bd6MHWA%GpPSq5UZ4K3hXkfmCFaVeoIs2jtJwzynu1YclT7}{Lxiv[]/\\()|?*r^<>+;~!=\",_-' "; | ||
unsigned long w = width; | ||
unsigned long h = height/2; | ||
unsigned long rw = width / w; | ||
unsigned long rh = height / h; | ||
unsigned char r, g, b; | ||
unsigned char color; | ||
unsigned int ind; | ||
Rect rect; | ||
float weit; | ||
for(unsigned long y = 0; y < h; ++y) { | ||
if ((y+1)*rh > height) { | ||
break; | ||
} | ||
for(unsigned long x = 0; x < w; ++x) { | ||
if ((x+1)*rw > width) { | ||
break; | ||
} | ||
rect.x = x * rw; | ||
rect.y = y * rh; | ||
rect.w = rw; | ||
rect.h = rh; | ||
|
||
calcWeit(img, width * 4, rect, r, g, b); | ||
|
||
weit = ((float)(r + g + b)) / 3.0f / 255.0f; | ||
|
||
weit = 1-weit; | ||
weit = weit * weit; | ||
weit = 1-weit; | ||
color = rgb_to_x256(r, g, b); | ||
ind = weit * (ASCII.size() - 1); | ||
printf("\x1B[38;5;%im%c\x1B[0m", color, ASCII[ind]); | ||
} | ||
printf("\n"); | ||
} | ||
fflush(stdout); | ||
} | ||
#include "ascii.hh" | ||
|
||
int main(int argc, char** argv) { | ||
struct NSVGimage* image; | ||
unsigned long w, h; | ||
float scalex, scaley; | ||
unsigned long w = 0, h = 0; | ||
if (argc < 2) { | ||
printf("Usage: %s <svg filename> [<width> [<height>]]\n", argv[0]); | ||
return 1; | ||
} | ||
image = nsvgParseFromFile(argv[1], "px", 96); | ||
if (image == NULL) { | ||
printf("Error loading image %s\n", argv[1]); | ||
return 1; | ||
} | ||
|
||
if (argc < 3) { | ||
w = image->width; | ||
} else { | ||
|
||
if (argc > 2) { | ||
w = atoi(argv[2]); | ||
} | ||
scalex = ((float)w)/image->width; | ||
|
||
if (argc < 4) { | ||
scaley = scalex; | ||
if (image->width == image->height) { | ||
h = w; | ||
} else { | ||
h = scaley*image->height; | ||
} | ||
} else { | ||
|
||
if (argc > 3) { | ||
h = atoi(argv[3]); | ||
scaley = ((float)h)/image->height; | ||
} | ||
// Rasterize | ||
unsigned char* img = new unsigned char[w*h*4]; | ||
NSVGrasterizer* rast = nsvgCreateRasterizer(); | ||
nsvgRasterizeStretched(rast, image, 0,0,scalex, scaley, img, w, h, w*4); | ||
nsvgDeleteRasterizer(rast); | ||
printArt(img, w, h); | ||
delete[] img; | ||
|
||
Rect r = {0, 0, w, h/2}; | ||
AsciiImage image(argv[1], r, argc < 3, argc < 4); | ||
image.print(); | ||
|
||
return 0; | ||
} |