From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: zsh-workers-return-43763-ml=inbox.vuxu.org@zsh.org X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from primenet.com.au (ns1.primenet.com.au [203.24.36.2]) by inbox.vuxu.org (OpenSMTPD) with ESMTP id 2d758329 for ; Mon, 5 Nov 2018 14:04:01 +0000 (UTC) Received: (qmail 9596 invoked by alias); 5 Nov 2018 14:03:50 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 43763 Received: (qmail 14533 invoked by uid 1010); 5 Nov 2018 14:03:50 -0000 X-Qmail-Scanner-Diagnostics: from mail-ot1-f68.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(209.85.210.68):SA:0(-2.0/5.0):. Processed in 3.276291 secs); 05 Nov 2018 14:03:50 -0000 X-Envelope-From: sgniazdowski@gmail.com X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=qnkcIXz11/Oh3/jpeS+HcmJ8vgQSYrdr2Ij+TMeXZ0A=; b=CJVlKMlunP5ierH/LNeKndiIGIIhOCCOLfkcXthA44Hj/g6K97fv/tgeF/lL9VIG14 NjuV6qbsxD/Jd28wQQHTlUdAvszxys9UJBKcmimEVrM5hpL9qoNKqmWHA503Qu3bOcab 8MGFvKyPZbDSJzyqGRcU4siArGa3Om5HStTQRdbKXC45bpY9CBjZIbnUjOYBMW09agUe ARMWVaoxYpM3h2GZGmo8uIRZWobDNy2YQTPk7BWr2J4ewYxXMNH6yn7XZhZYC90K+5Bt 4T5refXUd5N3WEFkBI5a2yqxvTanX4KB/Tqt5zWwkzIfMvcJTfe5G/e34o5tpP6wM55b OjMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=qnkcIXz11/Oh3/jpeS+HcmJ8vgQSYrdr2Ij+TMeXZ0A=; b=j4vmap/2aLW5XqSltwSe68Psfdybc8SXPi+KsVpr3qRByXjBna6dx4bpK6GHQVJjVv 2xixzAY6bHGLoisdAmSZAOf0R7r6vOJO/ODGnBK0AWM8yPMjdkU9H4V/lhkqwlbwr7Ph 3Wmti++AaCkyLyAhWJF+iELefruLonb3KpdUAOTxe2zlpBvNspcDlmJ03i/F5yJ2rHti mnsk7z1HUJcWrh8ZSYKZasuBjSprDrTcIFZo4tCvz6losvKJ00/o4hozQI6s2I+H9Z2U NQ4boqNU9FWopAw3obrLnzUBHLJaFGJf3bhe8TU6OrNCETssnkvI/KjSDkytJ/s0jo0L RgWg== X-Gm-Message-State: AGRZ1gJ4/wnqfY0lxrd5WC2A67c8z/xDec6IgfPCULV1C4GA9FTBaEYK 3wEeimVKaqWaXxhoYYfK9u3TAvw2gEP5YqD2HhM= X-Google-Smtp-Source: AJdET5cfla2cIJvSXmou1GP7UzIwh8HVEFNodPg5GHGU3JhOEzctac2Q+LkRYXrHc8tfKp1nXNHegP4uLJttYZR0Dos= X-Received: by 2002:a9d:4201:: with SMTP id q1mr12375391ote.126.1541426623650; Mon, 05 Nov 2018 06:03:43 -0800 (PST) MIME-Version: 1.0 References: <58111-1540942908.680582@SXAw.BXd_.-x5N> In-Reply-To: <58111-1540942908.680582@SXAw.BXd_.-x5N> From: Sebastian Gniazdowski Date: Mon, 5 Nov 2018 15:03:31 +0100 Message-ID: Subject: =?UTF-8?Q?Re=3A_Terminal_theme_tool_=E2=80=93_a_workaround_for_lack_of?= =?UTF-8?Q?_24=2Dbit_color_in_Zsh=3F?= To: Oliver Kiddle Cc: Zsh hackers list Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Wed, 31 Oct 2018 at 00:41, Oliver Kiddle wrote: > The nearest colour matching code converts RGB colours to CIE L*a*b*. > There are more sophisticated methods but this does a noticably better > job than just taking distances in RGB space. It doesn't use any of the I have an example of CIELab vs. naive nearest-finding (I've earlier used code from Oliver). On the page: http://psprint.blinkenshell.org/palette.xhtml there are two palettes, one converted by CIELab, the second one with the naive method. The source of the original colors is this theme: https://www.syntaxenvy.com/0063633. The point: Syntax-Envy generates colors with the same lightness. And CIELab successfully preserves that property. Just looking at the 6 colors reveals this, they are quite the same bright. Naive conversion openly breaks down, first SteelBlue3, which is quite dark, then DarkKhaki, which is very dark, and then the repeated LightSkyBlue3, where CIELab found a distinct candidate =E2=80=93 CadetBlue. To make this post little less dependent on an external web page, the CIELab palette is: LightSkyBlue1, LightSkyBlue3, LightGoldenrod2, CadetBlue, PaleGreen3, LightSkyBlue3, Grey11, while the naive palette is: LightSkyBlue3, SteelBlue3, DarkKhaki, LightSkyBlue3, PaleGreen3, LightSkyBlue3, Grey0 . > I'd be interested if anyone has any thoughts or views on any of this? > > Oliver > > PS. Thanks to Sebastian for testing out an earlier version of the > colour matching code. > > 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 > + > +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 =3D (float)red / 255.0; > + float G =3D (float)green / 255.0; > + float B =3D (float)blue / 255.0; > + R =3D 100.0 * (R > 0.04045 ? powf((R + 0.055) / 1.055, 2.4) : R / 12= .92); > + G =3D 100.0 * (G > 0.04045 ? powf((G + 0.055) / 1.055, 2.4) : G / 12= .92); > + B =3D 100.0 * (B > 0.04045 ? powf((B + 0.055) / 1.055, 2.4) : B / 12= .92); > + > + /* Observer. =3D 2 degrees, Illuminant =3D D65 */ > + float X =3D (R * 0.4124 + G * 0.3576 + B * 0.1805) / 95.047; > + float Y =3D (R * 0.2126 + G * 0.7152 + B * 0.0722) / 100.0; > + float Z =3D (R * 0.0193 + G * 0.1192 + B * 0.9505) / 108.883; > + > + X =3D (X > 0.008856) ? powf(X, 1.0/3.0) : (7.787 * X) + (16.0 / 116.= 0); > + Y =3D (Y > 0.008856) ? powf(Y, 1.0/3.0) : (7.787 * Y) + (16.0 / 116.= 0); > + Z =3D (Z > 0.008856) ? powf(Z, 1.0/3.0) : (7.787 * Z) + (16.0 / 116.= 0); > + > + lab->L =3D (116.0 * Y) - 16.0; > + lab->a =3D 500.0 * (X - Y); > + lab->b =3D 200.0 * (Y - Z); > +} > + > +static int > +mapRGBto88(int red, int green, int blue) > +{ > + int component[] =3D { 0, 0x8b, 0xcd, 0xff, 0x2e, 0x5c, 0x8b, 0xa2, 0= xb9, 0xd0, 0xe7 }; > + struct cielab orig, next; > + float nextl, bestl =3D -1; > + int r, g, b; > + int comp_r =3D 0, comp_g =3D 0, comp_b =3D 0; > + > + /* Get original value */ > + RGBtoLAB(red, green, blue, &orig); > + > + /* try every one of the 72 colours */ > + for (r =3D 0; r < 11; r++) { > + for (g =3D 0; g <=3D 3; g++) { > + for (b =3D 0; b <=3D 3; b++) { > + if (r > 3) g =3D b =3D r; /* advance inner loops to the b= lock of greys */ > + RGBtoLAB(component[r], component[g], component[b], &next)= ; > + nextl =3D deltae(&orig, &next); > + if (nextl < bestl || bestl < 0) { > + bestl =3D nextl; > + comp_r =3D r; > + comp_g =3D g; > + comp_b =3D 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[] =3D { > + 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 =3D -1; > + int r, g, b; > + int comp_r =3D 0, comp_g =3D 0, comp_b =3D 0; > + > + /* Get original value */ > + RGBtoLAB(red, green, blue, &orig); > + > + for (r =3D 0; r < sizeof(component)/sizeof(*component); r++) { > + for (g =3D 0; g <=3D 5; g++) { > + for (b =3D 0; b <=3D 5; b++) { > + if (r > 5) g =3D b =3D r; /* advance inner loops to the b= lock of greys */ > + RGBtoLAB(component[r], component[g], component[b], &next)= ; > + nextl =3D deltae(&orig, &next); > + if (nextl < bestl || bestl < 0) { > + bestl =3D nextl; > + comp_r =3D r; > + comp_g =3D g; > + comp_b =3D 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 =3D=3D 256) > + return mapRGBto256(col->red, col->green, col->blue); > + if (tccolours =3D=3D 88) > + return mapRGBto88(col->red, col->green, col->blue); > + return 0; > +} > + > +static struct features module_features =3D { > + NULL, 0, > + NULL, 0, > + NULL, 0, > + NULL, 0, > + 0 > +}; > + > +/**/ > +int > +setup_(UNUSED(Module m)) > +{ > + return 0; > +} > + > +/**/ > +int > +features_(Module m, char ***features) > +{ > + *features =3D 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=3Dzsh/nearcolor > +link=3Ddynamic > +load=3Dno > + > +objects=3D"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[] =3D { > 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 =3D 0, tc; > > if (teststrp) { > - if ((named =3D ialpha(**teststrp))) { > + if (**teststrp =3D=3D '#' && isxdigit((*teststrp)[1])) { > + struct color_rgb color; > + char *end; > + zlong col =3D zstrtol(*teststrp+1, &end, 16); > + if (end - *teststrp =3D=3D 4) { > + color.red =3D col >> 8 | ((col >> 8) << 4); > + color.green =3D (col & 0xf0) >> 4; > + color.green |=3D color.green << 4; > + color.blue =3D col & 0xf; > + color.blue |=3D color.blue << 4; > + } else if (end - *teststrp =3D=3D 7) { > + color.red =3D col >> 16; > + color.green =3D (col & 0xff00) >> 8; > + color.blue =3D col & 0xff; > + } > + *teststrp =3D end; > + colour =3D runhookdef(GETCOLORATTR, &color); > + } else if ((named =3D ialpha(**teststrp))) { > colour =3D match_named_colour(teststrp); > if (colour =3D=3D 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() */ --=20 Sebastian Gniazdowski News: https://twitter.com/ZdharmaI IRC: https://kiwiirc.com/client/chat.freenode.net:+6697/#zplugin Blog: http://zdharma.org