summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Kiddle <okiddle@yahoo.co.uk>2018-11-05 22:24:05 +0100
committerOliver Kiddle <okiddle@yahoo.co.uk>2018-11-05 22:24:05 +0100
commit37d0005a9e651f366076c930019464d162506a57 (patch)
tree11af20db3184af2a860791a9014e3b474de957a1
parent916ba78cd7c30800e7025bb91e735e27320fff29 (diff)
downloadzsh-37d0005a9e651f366076c930019464d162506a57.tar.gz
zsh-37d0005a9e651f366076c930019464d162506a57.tar.xz
zsh-37d0005a9e651f366076c930019464d162506a57.zip
43747: new module to map colours from hex triplets to the nearest matching colour
-rw-r--r--ChangeLog6
-rw-r--r--Src/Modules/nearcolor.c178
-rw-r--r--Src/Modules/nearcolor.mdd5
-rw-r--r--Src/init.c1
-rw-r--r--Src/prompt.c19
-rw-r--r--Src/zsh.h7
6 files changed, 215 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 1dfcb58f9..d224f7840 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2018-11-05  Oliver Kiddle  <okiddle@yahoo.co.uk>
+
+	* 43747: Src/Modules/nearcolor.c, Src/Modules/nearcolor.mdd,
+	Src/init.c, Src/prompt.c, Src/zsh.h: new module to map
+	colours from hex triplets to the nearest matching colour
+
 2018-11-03  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
 	* 43752: Completion/Base/Completer/_expand: Fix quoting if
diff --git a/Src/Modules/nearcolor.c b/Src/Modules/nearcolor.c
new file mode 100644
index 000000000..2a763d470
--- /dev/null
+++ b/Src/Modules/nearcolor.c
@@ -0,0 +1,178 @@
+#include "nearcolor.mdh"
+#include "nearcolor.pro"
+
+#include <math.h>
+
+struct cielab {
+    float L, a, b;
+};
+typedef struct cielab *Cielab;
+
+static float
+deltae(Cielab lab1, Cielab lab2)
+{
+    /* taking square root unnecessary as we're just comparing values */
+    return powf(lab1->L - lab2->L, 2) +
+	powf(lab1->a - lab2->a, 2) +
+	powf(lab1->b - lab2->b, 2);
+}
+
+static void
+RGBtoLAB(int red, int green, int blue, Cielab lab)
+{
+    float R = (float)red / 255.0;
+    float G = (float)green / 255.0;
+    float B = (float)blue / 255.0;
+    R = 100.0 * (R > 0.04045 ? powf((R + 0.055) / 1.055, 2.4) : R / 12.92);
+    G = 100.0 * (G > 0.04045 ? powf((G + 0.055) / 1.055, 2.4) : G / 12.92);
+    B = 100.0 * (B > 0.04045 ? powf((B + 0.055) / 1.055, 2.4) : B / 12.92);
+
+    /* Observer. = 2 degrees, Illuminant = D65 */
+    float X = (R * 0.4124 + G * 0.3576 + B * 0.1805) / 95.047;
+    float Y = (R * 0.2126 + G * 0.7152 + B * 0.0722) / 100.0;
+    float Z = (R * 0.0193 + G * 0.1192 + B * 0.9505) / 108.883;
+
+    X = (X > 0.008856) ? powf(X, 1.0/3.0) : (7.787 * X) + (16.0 / 116.0);
+    Y = (Y > 0.008856) ? powf(Y, 1.0/3.0) : (7.787 * Y) + (16.0 / 116.0);
+    Z = (Z > 0.008856) ? powf(Z, 1.0/3.0) : (7.787 * Z) + (16.0 / 116.0);
+
+    lab->L = (116.0 * Y) - 16.0;
+    lab->a = 500.0 * (X - Y);
+    lab->b = 200.0 * (Y - Z);
+}
+
+static int
+mapRGBto88(int red, int green, int blue)
+{
+    int component[] = { 0, 0x8b, 0xcd, 0xff, 0x2e, 0x5c, 0x8b, 0xa2, 0xb9, 0xd0, 0xe7 };
+    struct cielab orig, next;
+    float nextl, bestl = -1;
+    int r, g, b;
+    int comp_r = 0, comp_g = 0, comp_b = 0;
+
+    /* Get original value */
+    RGBtoLAB(red, green, blue, &orig);
+
+    /* try every one of the 72 colours */
+    for (r = 0; r < 11; r++) {
+	for (g = 0; g <= 3; g++) {
+	    for (b = 0; b <= 3; b++) {
+		if (r > 3) g = b = r; /* advance inner loops to the block of greys */
+		RGBtoLAB(component[r], component[g], component[b], &next);
+		nextl = deltae(&orig, &next);
+		if (nextl < bestl || bestl < 0) {
+		    bestl = nextl;
+		    comp_r = r;
+		    comp_g = g;
+		    comp_b = b;
+		}
+	    }
+	}
+    }
+
+    return (comp_r > 3) ? 77 + comp_r :
+        16 + (comp_r * 16) + (comp_g * 4) + comp_b;
+}
+
+/*
+ * Convert RGB to nearest colour in the 256 colour range
+ */
+static int
+mapRGBto256(int red, int green, int blue)
+{
+    int component[] = {
+	0, 0x5f, 0x87, 0xaf, 0xd7, 0xff,
+	0x8, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e,
+	0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, 0x9e,
+	0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee
+    };
+    struct cielab orig, next;
+    float nextl, bestl = -1;
+    int r, g, b;
+    int comp_r = 0, comp_g = 0, comp_b = 0;
+
+    /* Get original value */
+    RGBtoLAB(red, green, blue, &orig);
+
+    for (r = 0; r < sizeof(component)/sizeof(*component); r++) {
+	for (g = 0; g <= 5; g++) {
+	    for (b = 0; b <= 5; b++) {
+		if (r > 5) g = b = r; /* advance inner loops to the block of greys */
+		RGBtoLAB(component[r], component[g], component[b], &next);
+		nextl = deltae(&orig, &next);
+		if (nextl < bestl || bestl < 0) {
+		    bestl = nextl;
+		    comp_r = r;
+		    comp_g = g;
+		    comp_b = b;
+		}
+	    }
+	}
+    }
+
+    return (comp_r > 5) ? 226 + comp_r :
+	16 + (comp_r * 36) + (comp_g * 6) + comp_b;
+}
+
+static int
+getnearestcolor(UNUSED(Hookdef dummy), Color_rgb col)
+{
+    if (tccolours == 256)
+	return mapRGBto256(col->red, col->green, col->blue);
+    if (tccolours == 88)
+	return mapRGBto88(col->red, col->green, col->blue);
+    return 0;
+}
+
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+    return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(Module m)
+{
+    addhookfunc("get_color_attr", (Hookfn) getnearestcolor);
+    return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    deletehookfunc("get_color_attr", (Hookfn) getnearestcolor);
+    return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+    return 0;
+}
diff --git a/Src/Modules/nearcolor.mdd b/Src/Modules/nearcolor.mdd
new file mode 100644
index 000000000..2fcdaf04e
--- /dev/null
+++ b/Src/Modules/nearcolor.mdd
@@ -0,0 +1,5 @@
+name=zsh/nearcolor
+link=dynamic
+load=no
+
+objects="nearcolor.o"
diff --git a/Src/init.c b/Src/init.c
index cec914329..e7e62e2f7 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -94,6 +94,7 @@ mod_export struct hookdef zshhooks[] = {
     HOOKDEF("exit", NULL, HOOKF_ALL),
     HOOKDEF("before_trap", NULL, HOOKF_ALL),
     HOOKDEF("after_trap", NULL, HOOKF_ALL),
+    HOOKDEF("get_color_attr", NULL, HOOKF_ALL),
 };
 
 /* keep executing lists until EOF found */
diff --git a/Src/prompt.c b/Src/prompt.c
index 959ed8e3d..39edbdb2b 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -1621,7 +1621,24 @@ match_colour(const char **teststrp, int is_fg, int colour)
     int shft, on, named = 0, tc;
 
     if (teststrp) {
-	if ((named = ialpha(**teststrp))) {
+	if (**teststrp == '#' && isxdigit((*teststrp)[1])) {
+	    struct color_rgb color;
+	    char *end;
+	    zlong col = zstrtol(*teststrp+1, &end, 16);
+            if (end - *teststrp == 4) {
+		color.red = col >> 8 | ((col >> 8) << 4);
+		color.green = (col & 0xf0) >> 4;
+		color.green |= color.green << 4;
+		color.blue = col & 0xf;
+		color.blue |= color.blue << 4;
+	    } else if (end - *teststrp == 7) {
+		color.red = col >> 16;
+		color.green = (col & 0xff00) >> 8;
+		color.blue = col & 0xff;
+	    }
+	    *teststrp = end;
+	    colour = runhookdef(GETCOLORATTR, &color);
+	} else if ((named = ialpha(**teststrp))) {
 	    colour = match_named_colour(teststrp);
 	    if (colour == 8) {
 		/* default */
diff --git a/Src/zsh.h b/Src/zsh.h
index 894158818..68731e226 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2707,6 +2707,12 @@ struct ttyinfo {
 #define COL_SEQ_BG	(1)
 #define COL_SEQ_COUNT	(2)
 
+struct color_rgb {
+    unsigned int red, green, blue;
+};
+
+typedef struct color_rgb *Color_rgb;
+
 /*
  * Flags to testcap() and set_colour_attribute (which currently only
  * handles TSC_PROMPT).
@@ -3203,6 +3209,7 @@ enum {
 #define EXITHOOK       (zshhooks + 0)
 #define BEFORETRAPHOOK (zshhooks + 1)
 #define AFTERTRAPHOOK  (zshhooks + 2)
+#define GETCOLORATTR   (zshhooks + 3)
 
 #ifdef MULTIBYTE_SUPPORT
 /* Final argument to mb_niceformat() */