diff options
Diffstat (limited to 'bin/scheme.c')
-rw-r--r-- | bin/scheme.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/bin/scheme.c b/bin/scheme.c new file mode 100644 index 00000000..2bae8f82 --- /dev/null +++ b/bin/scheme.c @@ -0,0 +1,278 @@ +/* Copyright (C) 2018, 2019 June McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "png.h" + +typedef unsigned uint; +typedef unsigned char byte; + +struct HSV { + double h, s, v; +}; + +struct RGB { + byte r, g, b; +}; + +static struct RGB convert(struct HSV o) { + double c = o.v * o.s; + double h = o.h / 60.0; + double x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0)); + double m = o.v - c; + double r = m, g = m, b = m; + if (h <= 1.0) { r += c; g += x; } + else if (h <= 2.0) { r += x; g += c; } + else if (h <= 3.0) { g += c; b += x; } + else if (h <= 4.0) { g += x; b += c; } + else if (h <= 5.0) { r += x; b += c; } + else if (h <= 6.0) { r += c; b += x; } + return (struct RGB) { r * 255.0, g * 255.0, b * 255.0 }; +} + +static const struct HSV +R = { 0.0, 1.0, 1.0 }, +Y = { 60.0, 1.0, 1.0 }, +G = { 120.0, 1.0, 1.0 }, +C = { 180.0, 1.0, 1.0 }, +B = { 240.0, 1.0, 1.0 }, +M = { 300.0, 1.0, 1.0 }; + +static struct HSV x(struct HSV o, double hd, double sf, double vf) { + return (struct HSV) { + fmod(o.h + hd, 360.0), + fmin(o.s * sf, 1.0), + fmin(o.v * vf, 1.0), + }; +} + +enum { + Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, + Dark = 0, + Light = 8, + Background = 16, + Foreground, + Bold, + Selection, + Cursor, + SchemeLen, +}; +static struct HSV scheme[SchemeLen]; +static struct HSV *dark = &scheme[Dark]; +static struct HSV *light = &scheme[Light]; + +static void generate(void) { + light[Black] = x(R, +45.0, 0.3, 0.3); + light[Red] = x(R, +10.0, 0.9, 0.8); + light[Green] = x(G, -55.0, 0.8, 0.6); + light[Yellow] = x(Y, -20.0, 0.8, 0.8); + light[Blue] = x(B, -55.0, 0.4, 0.5); + light[Magenta] = x(M, +45.0, 0.4, 0.6); + light[Cyan] = x(C, -60.0, 0.3, 0.6); + light[White] = x(R, +45.0, 0.3, 0.8); + + dark[Black] = x(light[Black], 0.0, 1.0, 0.3); + dark[White] = x(light[White], 0.0, 1.0, 0.75); + for (uint i = Red; i < White; ++i) { + dark[i] = x(light[i], 0.0, 1.0, 0.8); + } + + scheme[Background] = x(dark[Black], 0.0, 1.0, 0.9); + scheme[Foreground] = x(light[White], 0.0, 1.0, 0.9); + scheme[Bold] = x(light[White], 0.0, 1.0, 1.0); + scheme[Selection] = x(light[Red], +10.0, 1.0, 0.8); + scheme[Cursor] = x(dark[White], 0.0, 1.0, 0.8); +} + +static void swap(struct HSV *a, struct HSV *b) { + struct HSV c = *a; + *a = *b; + *b = c; +} + +static void invert(void) { + swap(&dark[Black], &light[White]); + swap(&dark[White], &light[Black]); +} + +typedef void OutputFn(const struct HSV *hsv, uint len); + +static void outputHSV(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + printf("%g,%g,%g\n", hsv[i].h, hsv[i].s, hsv[i].v); + } +} + +#define FORMAT_RGB "%02hhX%02hhX%02hhX" + +static void outputRGB(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf(FORMAT_RGB "\n", rgb.r, rgb.g, rgb.b); + } +} + +static void outputLinux(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf("\x1B]P%X" FORMAT_RGB, i, rgb.r, rgb.g, rgb.b); + } +} + +static const char *Enum[SchemeLen] = { + "DarkBlack", "DarkRed", "DarkGreen", "DarkYellow", + "DarkBlue", "DarkMagenta", "DarkCyan", "DarkWhite", + "LightBlack", "LightRed", "LightGreen", "LightYellow", + "LightBlue", "LightMagenta", "LightCyan", "LightWhite", + "Background", "Foreground", "Bold", "Selection", "Cursor", +}; + +static void outputEnum(const struct HSV *hsv, uint len) { + printf("enum {\n"); + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf("\t%s = 0x" FORMAT_RGB ",\n", Enum[i], rgb.r, rgb.g, rgb.b); + } + printf("};\n"); +} + +#define FORMAT_X "rgb:%02hhX/%02hhX/%02hhX" + +static const char *Resources[SchemeLen] = { + [Background] = "background", + [Foreground] = "foreground", + [Bold] = "colorBD", + [Selection] = "highlightColor", + [Cursor] = "cursorColor", +}; + +static void outputXTerm(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + if (Resources[i]) { + printf("XTerm*%s: " FORMAT_X "\n", Resources[i], rgb.r, rgb.g, rgb.b); + } else { + printf("XTerm*color%u: " FORMAT_X "\n", i, rgb.r, rgb.g, rgb.b); + } + } +} + +static const char *Mintty[SchemeLen] = { + "Black", "Red", "Green", "Yellow", + "Blue", "Magenta", "Cyan", "White", + "BoldBlack", "BoldRed", "BoldGreen", "BoldYellow", + "BoldBlue", "BoldMagenta", "BoldCyan", "BoldWhite", + [Background] = "BackgroundColour", + [Foreground] = "ForegroundColour", + [Cursor] = "CursorColour", +}; + +static void outputMintty(const struct HSV *hsv, uint len) { + for (uint i = 0; i < len; ++i) { + if (!Mintty[i]) continue; + struct RGB rgb = convert(hsv[i]); + printf("%s=%hhu,%hhu,%hhu\n", Mintty[i], rgb.r, rgb.g, rgb.b); + } +} + +static void outputCSS(const struct HSV *hsv, uint len) { + printf(":root {\n"); + for (uint i = 0; i < len; ++i) { + struct RGB rgb = convert(hsv[i]); + printf("\t--ansi%u: #" FORMAT_RGB ";\n", i, rgb.r, rgb.g, rgb.b); + } + printf("}\n"); + for (uint i = 0; i < len; ++i) { + printf( + ".fg%u { color: var(--ansi%u); }\n" + ".bg%u { background-color: var(--ansi%u); }\n", + i, i, i, i + ); + } +} + +enum { + SwatchWidth = 64, + SwatchHeight = 64, + SwatchCols = 8, +}; + +static void outputPNG(const struct HSV *hsv, uint len) { + uint rows = (len + SwatchCols - 1) / SwatchCols; + uint width = SwatchWidth * SwatchCols; + uint height = SwatchHeight * rows; + pngHead(stdout, width, height, 8, PNGIndexed); + + struct RGB pal[len]; + for (uint i = 0; i < len; ++i) { + pal[i] = convert(hsv[i]); + } + pngPalette(stdout, (byte *)pal, sizeof(pal)); + + byte data[height][1 + width]; + memset(data, 0, sizeof(data)); + for (uint y = 0; y < height; ++y) { + data[y][0] = (y % SwatchHeight ? PNGUp : PNGSub); + } + for (uint i = 0; i < len; ++i) { + uint y = SwatchHeight * (i / SwatchCols); + uint x = SwatchWidth * (i % SwatchCols); + data[y][1 + x] = (x ? 1 : i); + } + pngData(stdout, (byte *)data, sizeof(data)); + pngTail(stdout); +} + +int main(int argc, char *argv[]) { + generate(); + + OutputFn *output = outputRGB; + const struct HSV *hsv = scheme; + uint len = 16; + + int opt; + while (0 < (opt = getopt(argc, argv, "Xacghilmp:stx"))) { + switch (opt) { + break; case 'X': output = outputXTerm; + break; case 'a': len = 16; + break; case 'c': output = outputEnum; + break; case 'g': output = outputPNG; + break; case 'h': output = outputHSV; + break; case 'i': invert(); + break; case 'l': output = outputLinux; + break; case 'm': output = outputMintty; + break; case 'p': { + uint p = strtoul(optarg, NULL, 0); + if (p >= SchemeLen) return EX_USAGE; + hsv = &scheme[p]; + len = 1; + } + break; case 's': output = outputCSS; + break; case 't': len = SchemeLen; + break; case 'x': output = outputRGB; + break; default: return EX_USAGE; + } + } + + output(hsv, len); +} |