diff options
| author | david <[email protected]> | 2026-10-13 07:43:48 -0300 |
|---|---|---|
| committer | david <[email protected]> | 2026-10-13 07:43:48 -0300 |
| commit | f0f954d463f7ab0e0ce792527e6567611ed67bf9 (patch) | |
| tree | 862c75c87380f67c11c865797faa9cc0c25a5970 /src/main.c | |
| parent | 3b16b2141ef5ce085ce50b737bc53ee3f7e556f3 (diff) | |
| download | mikusays-c-f0f954d463f7ab0e0ce792527e6567611ed67bf9.tar.gz mikusays-c-f0f954d463f7ab0e0ce792527e6567611ed67bf9.zip | |
50% colors i'll do rainbow and whatever else i'm missing from the rust version later too lseepy rn
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 119 |
1 files changed, 116 insertions, 3 deletions
@@ -160,7 +160,12 @@ static void print_help(void){ " -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\n" " -l, --list List all available art styles with their indices\n" " -h, --help Print help\n" - " -V, --version Print version\n"); + " -V, --version Print version\n" + " --rainbow Apply a smooth rainbow gradient across the art\n" + " --saturation <0-100> Rainbow saturation (default: 100)\n" + " --brightness <0-100> Rainbow brightness (default: 50)\n" + " --color <COLOR> Set a single solid color\n" + " --no-color Disable all coloring\n"); } static void list_all_art_styles(void){ @@ -177,6 +182,61 @@ static void list_all_art_styles(void){ printf("\nUse --style <number> to select a specific style, or omit for random selection.\n"); } +static struct colorconfig colors; +static int rainbow = 0, has_solid = 0; +static rgbcolor solid; + +static int is_art_space(unsigned long codepoint){ + return (codepoint >= 0x09 && codepoint <= 0x0D) || codepoint == 0x20 || codepoint == 0x85 || codepoint == 0xA0 + || codepoint == 0x1680 || (codepoint >= 0x2000 && codepoint <= 0x200A) || codepoint == 0x2028 + || codepoint == 0x2029 || codepoint == 0x202F || codepoint == 0x205F || codepoint == 0x3000; +} + +static size_t count_colored_chars(char **art){ + size_t total = 0; + for(int i = 0; art[i] != NULL; i++){ + const unsigned char *p = (const unsigned char *)art[i]; + while(*p){ + unsigned long codepoint; + p += utf8_decode(p, &codepoint); + if(!is_art_space(codepoint)) total++; + } + } + return total; +} + +static void print_art_line(const char *line, size_t *index, size_t total){ + char esc[32]; + const unsigned char *p = (const unsigned char *)line; + + if(colors.mode == MODE_NONE || (!rainbow && !has_solid)){ + fputs(line, stdout); + return; + } + if(!rainbow){//solid: one color for the whole line + rgb_to_fg_escape(solid, colors.mode, esc, sizeof(esc)); + printf("%s%s\x1b[0m", esc, line); + return; + } + while(*p){//rainbow: one color per non-space char + unsigned long codepoint; + int len = utf8_decode(p, &codepoint); + if(is_art_space(codepoint)){ + fwrite(p, 1, (size_t)len, stdout); + }else{ + unsigned short hue = (unsigned short)((float)*index / (float)total * 360.0f + 0.5f); + rgbcolor c = hsl_to_rgb(hue, colors.saturation, colors.brightness); + rgb_to_fg_escape(c, colors.mode, esc, sizeof(esc)); + printf("%s", esc); + fwrite(p, 1, (size_t)len, stdout); + fputs("\x1b[0m", stdout); + (*index)++; + } + p += len; + } +} + + void draw_miku_says(char *text, int style){ char **speech_bubble_lines = getbubblelines(text); if(speech_bubble_lines == NULL) @@ -255,6 +315,8 @@ void draw_miku_says(char *text, int style){ for(int i = 0; i < top_padding; i++){ printf("\n"); } + size_t color_index = 0; + size_t colored_total = count_colored_chars(miku_art); for(int i = 0; all_content_lines[i] != NULL; i++){ int is_bubble_line = i < bubble_line_count; @@ -268,7 +330,14 @@ void draw_miku_says(char *text, int style){ else padding = overall_left_padding; - printf("%*s%s\n", padding, "", all_content_lines[i]); + if(is_bubble_line){ + printf("%*s%s\n", padding, "", all_content_lines[i]); + }else{ + printf("%*s", padding, ""); + print_art_line(all_content_lines[i], &color_index, colored_total); + putchar('\n'); + } + } for(int i = 0; i < top_padding; i++){ @@ -277,11 +346,13 @@ void draw_miku_says(char *text, int style){ freelines(speech_bubble_lines); free(all_content_lines); } - + int main(int argc, char **argv){ srand(time(NULL)); getterminalsize(); + colorconfig_init(&colors); + long style = -1; //-1 means random int list = 0; int ret = 0; @@ -317,12 +388,54 @@ int main(int argc, char **argv){ goto out; } style = v < 0 ? -1 : v; //negative = random + } else if(strcmp(argv[i], "--no-color") == 0){ + colors.mode = MODE_NONE; + } else if(strcmp(argv[i], "--rainbow") == 0){ + rainbow = 1; + } else if(strcmp(argv[i], "--color") == 0){ + if(i + 1 >= argc){ + fprintf(stderr, "error: a value is required for '--color <COLOR>' but none was supplied\n"); + ret = 2; + goto out; + } + i++; + ColorParseError err = parse_color(argv[i], &solid); + if(err != COLOR_OK){ + fprintf(stderr, "error: invalid color '%s': %s; expected a named color, #RRGGBB, #RGB, R,G,B, hsl(H,S%%,L%%), or ANSI value 0-255\n", + argv[i], color_parse_error_message(err)); + ret = 2; + goto out; + } + has_solid = 1; + } else if(strcmp(argv[i], "--saturation") == 0 || strcmp(argv[i], "--brightness") == 0){ + const char *flag = argv[i]; + if(i + 1 >= argc){ + fprintf(stderr, "error: a value is required for '%s <0-100>' but none was supplied\n", flag); + ret = 2; + goto out; + } + i++; + char *end; + long v = strtol(argv[i], &end, 10); + if(*argv[i] == '\0' || *end != '\0' || v < 0 || v > 100){ + fprintf(stderr, "error: invalid value '%s' for '%s <0-100>'\n", argv[i], flag); + ret = 2; + goto out; + } + if(flag[2] == 's') colors.saturation = (unsigned char)v; + else colors.brightness = (unsigned char)v; } else { strcat(text, argv[i]); strcat(text, " "); } } + if(rainbow && has_solid){ + fprintf(stderr, "error: --rainbow cannot be used with --color\n"); + ret = 2; + goto out; + } + if(list){ list_all_art_styles(); } else if(text[0] == '\0'){ |
