aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias EngdegÄrd2020-06-12 18:12:37 +0200
committerMattias EngdegÄrd2020-06-21 21:22:26 +0200
commit9fe2bdb88a4ebd4b2286c1c2a2a2ba7411af01b6 (patch)
tree0979ec4f38172e25a0420eca5b22650c249a80f4 /src
parent0792f8e4f0de2328c57d552a5845bdf77265a971 (diff)
downloademacs-9fe2bdb88a4ebd4b2286c1c2a2a2ba7411af01b6.tar.gz
emacs-9fe2bdb88a4ebd4b2286c1c2a2a2ba7411af01b6.zip
Consolidate #RGB string parsers
Use a single parser of color strings in the #RGB, rgb:R/G/B and rgbi:R/G/B formats, replacing four existing ones. Previously, error-checking was spotty, handling of the rgbi: format not always present, and normalization of the result was sometimes incorrect. * src/dispextern.h: New prototype. * src/xfaces.c (parse_hex_color_comp, parse_float_color_comp) (parse_color_spec, Finternal-color_values_from_color_spec): New functions. * test/src/xfaces-tests.el (xfaces-internal-color-values-from-color-spec): New test. * lisp/term/tty-colors.el (tty-color-standard-values): Use internal-color-values-from-color-spec, replacing old parser. * src/nsterm.m (ns_get_color): * src/w32fns.c (x_to_w32_color): * src/xterm.c (x_parse_color): Use parse_color_spec, replacing old parsers. (HEX_COLOR_NAME_LENGTH): Remove #define.
Diffstat (limited to 'src')
-rw-r--r--src/dispextern.h2
-rw-r--r--src/nsterm.m59
-rw-r--r--src/w32fns.c157
-rw-r--r--src/xfaces.c116
-rw-r--r--src/xterm.c51
5 files changed, 155 insertions, 230 deletions
diff --git a/src/dispextern.h b/src/dispextern.h
index 0b1f3d14aeb..e1d6eddc419 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3514,6 +3514,8 @@ void update_face_from_frame_parameter (struct frame *, Lisp_Object,
3514 Lisp_Object); 3514 Lisp_Object);
3515extern bool tty_defined_color (struct frame *, const char *, Emacs_Color *, 3515extern bool tty_defined_color (struct frame *, const char *, Emacs_Color *,
3516 bool, bool); 3516 bool, bool);
3517bool parse_color_spec (const char *,
3518 unsigned short *, unsigned short *, unsigned short *);
3517 3519
3518Lisp_Object tty_color_name (struct frame *, int); 3520Lisp_Object tty_color_name (struct frame *, int);
3519void clear_face_cache (bool); 3521void clear_face_cache (bool);
diff --git a/src/nsterm.m b/src/nsterm.m
index 3dc7e1db7c9..0e405fc0175 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -2341,9 +2341,6 @@ ns_get_color (const char *name, NSColor **col)
2341 See https://lists.gnu.org/r/emacs-devel/2009-07/msg01203.html. */ 2341 See https://lists.gnu.org/r/emacs-devel/2009-07/msg01203.html. */
2342{ 2342{
2343 NSColor *new = nil; 2343 NSColor *new = nil;
2344 static char hex[20];
2345 int scaling = 0;
2346 float r = -1.0, g, b;
2347 NSString *nsname = [NSString stringWithUTF8String: name]; 2344 NSString *nsname = [NSString stringWithUTF8String: name];
2348 2345
2349 NSTRACE ("ns_get_color(%s, **)", name); 2346 NSTRACE ("ns_get_color(%s, **)", name);
@@ -2386,51 +2383,31 @@ ns_get_color (const char *name, NSColor **col)
2386 } 2383 }
2387 2384
2388 /* First, check for some sort of numeric specification. */ 2385 /* First, check for some sort of numeric specification. */
2389 hex[0] = '\0'; 2386 unsigned short r16, g16, b16;
2390 2387 if (parse_color_spec (name, &r16, &g16, &b16))
2391 if (name[0] == '0' || name[0] == '1' || name[0] == '.') /* RGB decimal */
2392 { 2388 {
2393 NSScanner *scanner = [NSScanner scannerWithString: nsname]; 2389 *col = [NSColor colorForEmacsRed: r16 / 65535.0
2394 [scanner scanFloat: &r]; 2390 green: g16 / 65535.0
2395 [scanner scanFloat: &g]; 2391 blue: b16 / 65535.0
2396 [scanner scanFloat: &b]; 2392 alpha: 1.0];
2397 } 2393 unblock_input ();
2398 else if (!strncmp(name, "rgb:", 4)) /* A newer X11 format -- rgb:r/g/b */ 2394 return 0;
2399 scaling = (snprintf (hex, sizeof hex, "%s", name + 4) - 2) / 3;
2400 else if (name[0] == '#') /* An old X11 format; convert to newer */
2401 {
2402 int len = 0;
2403 while (isxdigit (name[len + 1]))
2404 len++;
2405 if (name[len + 1] == '\0' && len >= 1 && len <= 12 && len % 3 == 0)
2406 {
2407 scaling = len / 3;
2408 for (int i = 0; i < 3; i++)
2409 sprintf (hex + i * (scaling + 1), "%.*s/", scaling,
2410 name + 1 + i * scaling);
2411 hex[3 * (scaling + 1) - 1] = '\0';
2412 }
2413 } 2395 }
2414 2396 else if (name[0] == '0' || name[0] == '1' || name[0] == '.')
2415 if (hex[0])
2416 { 2397 {
2417 unsigned int rr, gg, bb; 2398 /* RGB decimal */
2418 float fscale = (1 << (scaling * 4)) - 1; 2399 NSScanner *scanner = [NSScanner scannerWithString: nsname];
2419 if (sscanf (hex, "%x/%x/%x", &rr, &gg, &bb)) 2400 float r, g, b;
2401 if ( [scanner scanFloat: &r] && r >= 0 && r <= 1
2402 && [scanner scanFloat: &g] && g >= 0 && g <= 1
2403 && [scanner scanFloat: &b] && b >= 0 && b <= 1)
2420 { 2404 {
2421 r = rr / fscale; 2405 *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2422 g = gg / fscale; 2406 unblock_input ();
2423 b = bb / fscale; 2407 return 0;
2424 } 2408 }
2425 } 2409 }
2426 2410
2427 if (r >= 0.0F)
2428 {
2429 *col = [NSColor colorForEmacsRed: r green: g blue: b alpha: 1.0];
2430 unblock_input ();
2431 return 0;
2432 }
2433
2434 /* Otherwise, color is expected to be from a list */ 2411 /* Otherwise, color is expected to be from a list */
2435 { 2412 {
2436 NSEnumerator *lenum, *cenum; 2413 NSEnumerator *lenum, *cenum;
diff --git a/src/w32fns.c b/src/w32fns.c
index e595b0285a7..ab864332e78 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -864,161 +864,14 @@ x_to_w32_color (const char * colorname)
864 864
865 block_input (); 865 block_input ();
866 866
867 if (colorname[0] == '#') 867 unsigned short r, g, b;
868 if (parse_color_spec (colorname, &r, &g, &b))
868 { 869 {
869 /* Could be an old-style RGB Device specification. */ 870 unblock_input ();
870 int size = strlen (colorname + 1); 871 /* Throw away the low 8 bits and return 0xBBGGRR. */
871 char *color = alloca (size + 1); 872 return make_fixnum ((b & 0xff00) << 8 | (g & 0xff00) | r >> 8);
872
873 strcpy (color, colorname + 1);
874 if (size == 3 || size == 6 || size == 9 || size == 12)
875 {
876 UINT colorval;
877 int i, pos;
878 pos = 0;
879 size /= 3;
880 colorval = 0;
881
882 for (i = 0; i < 3; i++)
883 {
884 char *end;
885 char t;
886 unsigned long value;
887
888 /* The check for 'x' in the following conditional takes into
889 account the fact that strtol allows a "0x" in front of
890 our numbers, and we don't. */
891 if (!isxdigit (color[0]) || color[1] == 'x')
892 break;
893 t = color[size];
894 color[size] = '\0';
895 value = strtoul (color, &end, 16);
896 color[size] = t;
897 if (errno == ERANGE || end - color != size)
898 break;
899 switch (size)
900 {
901 case 1:
902 value = value * 0x10;
903 break;
904 case 2:
905 break;
906 case 3:
907 value /= 0x10;
908 break;
909 case 4:
910 value /= 0x100;
911 break;
912 }
913 colorval |= (value << pos);
914 pos += 0x8;
915 if (i == 2)
916 {
917 unblock_input ();
918 XSETINT (ret, colorval);
919 return ret;
920 }
921 color = end;
922 }
923 }
924 }
925 else if (strnicmp (colorname, "rgb:", 4) == 0)
926 {
927 const char *color;
928 UINT colorval;
929 int i, pos;
930 pos = 0;
931
932 colorval = 0;
933 color = colorname + 4;
934 for (i = 0; i < 3; i++)
935 {
936 char *end;
937 unsigned long value;
938
939 /* The check for 'x' in the following conditional takes into
940 account the fact that strtol allows a "0x" in front of
941 our numbers, and we don't. */
942 if (!isxdigit (color[0]) || color[1] == 'x')
943 break;
944 value = strtoul (color, &end, 16);
945 if (errno == ERANGE)
946 break;
947 switch (end - color)
948 {
949 case 1:
950 value = value * 0x10 + value;
951 break;
952 case 2:
953 break;
954 case 3:
955 value /= 0x10;
956 break;
957 case 4:
958 value /= 0x100;
959 break;
960 default:
961 value = ULONG_MAX;
962 }
963 if (value == ULONG_MAX)
964 break;
965 colorval |= (value << pos);
966 pos += 0x8;
967 if (i == 2)
968 {
969 if (*end != '\0')
970 break;
971 unblock_input ();
972 XSETINT (ret, colorval);
973 return ret;
974 }
975 if (*end != '/')
976 break;
977 color = end + 1;
978 }
979 } 873 }
980 else if (strnicmp (colorname, "rgbi:", 5) == 0)
981 {
982 /* This is an RGB Intensity specification. */
983 const char *color;
984 UINT colorval;
985 int i, pos;
986 pos = 0;
987
988 colorval = 0;
989 color = colorname + 5;
990 for (i = 0; i < 3; i++)
991 {
992 char *end;
993 double value;
994 UINT val;
995 874
996 value = strtod (color, &end);
997 if (errno == ERANGE)
998 break;
999 if (value < 0.0 || value > 1.0)
1000 break;
1001 val = (UINT)(0x100 * value);
1002 /* We used 0x100 instead of 0xFF to give a continuous
1003 range between 0.0 and 1.0 inclusive. The next statement
1004 fixes the 1.0 case. */
1005 if (val == 0x100)
1006 val = 0xFF;
1007 colorval |= (val << pos);
1008 pos += 0x8;
1009 if (i == 2)
1010 {
1011 if (*end != '\0')
1012 break;
1013 unblock_input ();
1014 XSETINT (ret, colorval);
1015 return ret;
1016 }
1017 if (*end != '/')
1018 break;
1019 color = end + 1;
1020 }
1021 }
1022 /* I am not going to attempt to handle any of the CIE color schemes 875 /* I am not going to attempt to handle any of the CIE color schemes
1023 or TekHVC, since I don't know the algorithms for conversion to 876 or TekHVC, since I don't know the algorithms for conversion to
1024 RGB. */ 877 RGB. */
diff --git a/src/xfaces.c b/src/xfaces.c
index cf155288bd1..308509a0267 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -220,6 +220,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
220#include "sysstdio.h" 220#include "sysstdio.h"
221#include <sys/types.h> 221#include <sys/types.h>
222#include <sys/stat.h> 222#include <sys/stat.h>
223#include <math.h>
223 224
224#include "lisp.h" 225#include "lisp.h"
225#include "character.h" 226#include "character.h"
@@ -819,6 +820,120 @@ load_pixmap (struct frame *f, Lisp_Object name)
819 Color Handling 820 Color Handling
820 ***********************************************************************/ 821 ***********************************************************************/
821 822
823/* Parse hex color component at S ending right before E.
824 Set *DST to the value normalized so that the maximum for the
825 number of digits given becomes 65535, and return true on success,
826 false otherwise. */
827static bool
828parse_hex_color_comp (const char *s, const char *e, unsigned short *dst)
829{
830 int n = e - s;
831 if (n <= 0 || n > 4)
832 return false;
833 int val = 0;
834 for (; s < e; s++)
835 {
836 int digit;
837 if (*s >= '0' && *s <= '9')
838 digit = *s - '0';
839 else if (*s >= 'A' && *s <= 'F')
840 digit = *s - 'A' + 10;
841 else if (*s >= 'a' && *s <= 'f')
842 digit = *s - 'a' + 10;
843 else
844 return false;
845 val = (val << 4) | digit;
846 }
847 int maxval = (1 << (n * 4)) - 1;
848 *dst = (unsigned)val * 65535 / maxval;
849 return true;
850}
851
852/* Parse floating-point color component at S ending right before E.
853 Return the number if in the range [0,1]; otherwise -1. */
854static double
855parse_float_color_comp (const char *s, const char *e)
856{
857 char *end;
858 double x = strtod (s, &end);
859 return (end == e && x >= 0 && x <= 1) ? x : -1;
860}
861
862/* Parse S as a numeric color specification and set *R, *G and *B.
863 Return true on success, false on failure.
864 Recognized formats:
865
866 "#RGB", with R, G and B hex strings of equal length, 1-4 digits each
867 "rgb:R/G/B", with R, G and B hex strings, 1-4 digits each
868 "rgbi:R/G/B", with R, G and B numbers in [0,1]
869
870 The result is normalized to a maximum value of 65535 per component. */
871bool
872parse_color_spec (const char *s,
873 unsigned short *r, unsigned short *g, unsigned short *b)
874{
875 int len = strlen (s);
876 if (s[0] == '#')
877 {
878 if ((len - 1) % 3 == 0)
879 {
880 int n = (len - 1) / 3;
881 return ( parse_hex_color_comp (s + 1 + 0 * n, s + 1 + 1 * n, r)
882 && parse_hex_color_comp (s + 1 + 1 * n, s + 1 + 2 * n, g)
883 && parse_hex_color_comp (s + 1 + 2 * n, s + 1 + 3 * n, b));
884 }
885 }
886 else if (strncmp (s, "rgb:", 4) == 0)
887 {
888 char *sep1, *sep2;
889 return ((sep1 = strchr (s + 4, '/')) != NULL
890 && (sep2 = strchr (sep1 + 1, '/')) != NULL
891 && parse_hex_color_comp (s + 4, sep1, r)
892 && parse_hex_color_comp (sep1 + 1, sep2, g)
893 && parse_hex_color_comp (sep2 + 1, s + len, b));
894 }
895 else if (strncmp (s, "rgbi:", 5) == 0)
896 {
897 char *sep1, *sep2;
898 double red, green, blue;
899 if ((sep1 = strchr (s + 5, '/')) != NULL
900 && (sep2 = strchr (sep1 + 1, '/')) != NULL
901 && (red = parse_float_color_comp (s + 5, sep1)) >= 0
902 && (green = parse_float_color_comp (sep1 + 1, sep2)) >= 0
903 && (blue = parse_float_color_comp (sep2 + 1, s + len)) >= 0)
904 {
905 *r = lrint (red * 65535);
906 *g = lrint (green * 65535);
907 *b = lrint (blue * 65535);
908 return true;
909 }
910 }
911 return false;
912}
913
914DEFUN ("internal-color-values-from-color-spec",
915 Finternal_color_values_from_color_spec,
916 Sinternal_color_values_from_color_spec,
917 1, 1, 0,
918 doc: /* Parse STRING as a numeric color and return (RED GREEN BLUE).
919Recognised formats for STRING are:
920
921 #RGB, where R, G and B are hex numbers of equal length, 1-4 digits each
922 rgb:R/G/B, where R, G, and B are hex numbers, 1-4 digits each
923 rgbi:R/G/B, where R, G and B are floating-point numbers in [0,1]
924
925The result is normalized to a maximum value of 65535 per component,
926forming a list of three integers in [0,65535].
927If STRING is not in one of the above forms, return nil. */)
928 (Lisp_Object string)
929{
930 CHECK_STRING (string);
931 unsigned short r, g, b;
932 return (parse_color_spec (SSDATA (string), &r, &g, &b)
933 ? list3i (r, g, b)
934 : Qnil);
935}
936
822/* Parse RGB_LIST, and fill in the RGB fields of COLOR. 937/* Parse RGB_LIST, and fill in the RGB fields of COLOR.
823 RGB_LIST should contain (at least) 3 lisp integers. 938 RGB_LIST should contain (at least) 3 lisp integers.
824 Return true iff RGB_LIST is OK. */ 939 Return true iff RGB_LIST is OK. */
@@ -7018,4 +7133,5 @@ clear the face cache, see `clear-face-cache'. */);
7018 defsubr (&Sinternal_face_x_get_resource); 7133 defsubr (&Sinternal_face_x_get_resource);
7019 defsubr (&Sx_family_fonts); 7134 defsubr (&Sx_family_fonts);
7020#endif 7135#endif
7136 defsubr (&Sinternal_color_values_from_color_spec);
7021} 7137}
diff --git a/src/xterm.c b/src/xterm.c
index 7989cecec7f..6340700cb89 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -2376,8 +2376,6 @@ x_query_frame_background_color (struct frame *f, XColor *bgcolor)
2376 x_query_colors (f, bgcolor, 1); 2376 x_query_colors (f, bgcolor, 1);
2377} 2377}
2378 2378
2379#define HEX_COLOR_NAME_LENGTH 32
2380
2381/* On frame F, translate the color name to RGB values. Use cached 2379/* On frame F, translate the color name to RGB values. Use cached
2382 information, if possible. 2380 information, if possible.
2383 2381
@@ -2389,44 +2387,23 @@ x_query_frame_background_color (struct frame *f, XColor *bgcolor)
2389Status x_parse_color (struct frame *f, const char *color_name, 2387Status x_parse_color (struct frame *f, const char *color_name,
2390 XColor *color) 2388 XColor *color)
2391{ 2389{
2390 /* Don't pass #RGB strings directly to XParseColor, because that
2391 follows the X convention of zero-extending each channel
2392 value: #f00 means #f00000. We want the convention of scaling
2393 channel values, so #f00 means #ff0000, just as it does for
2394 HTML, SVG, and CSS. */
2395 unsigned short r, g, b;
2396 if (parse_color_spec (color_name, &r, &g, &b))
2397 {
2398 color->red = r;
2399 color->green = g;
2400 color->blue = b;
2401 return 1;
2402 }
2403
2392 Display *dpy = FRAME_X_DISPLAY (f); 2404 Display *dpy = FRAME_X_DISPLAY (f);
2393 Colormap cmap = FRAME_X_COLORMAP (f); 2405 Colormap cmap = FRAME_X_COLORMAP (f);
2394 struct color_name_cache_entry *cache_entry; 2406 struct color_name_cache_entry *cache_entry;
2395
2396 if (color_name[0] == '#')
2397 {
2398 /* Don't pass #RGB strings directly to XParseColor, because that
2399 follows the X convention of zero-extending each channel
2400 value: #f00 means #f00000. We want the convention of scaling
2401 channel values, so #f00 means #ff0000, just as it does for
2402 HTML, SVG, and CSS.
2403
2404 So we translate #f00 to rgb:f/0/0, which X handles
2405 differently. */
2406 char rgb_color_name[HEX_COLOR_NAME_LENGTH];
2407 int len = strlen (color_name);
2408 int digits_per_channel;
2409 if (len == 4)
2410 digits_per_channel = 1;
2411 else if (len == 7)
2412 digits_per_channel = 2;
2413 else if (len == 10)
2414 digits_per_channel = 3;
2415 else if (len == 13)
2416 digits_per_channel = 4;
2417 else
2418 return 0;
2419
2420 snprintf (rgb_color_name, sizeof rgb_color_name, "rgb:%.*s/%.*s/%.*s",
2421 digits_per_channel, color_name + 1,
2422 digits_per_channel, color_name + digits_per_channel + 1,
2423 digits_per_channel, color_name + 2 * digits_per_channel + 1);
2424
2425 /* The rgb form is parsed directly by XParseColor without
2426 talking to the X server. No need for caching. */
2427 return XParseColor (dpy, cmap, rgb_color_name, color);
2428 }
2429
2430 for (cache_entry = FRAME_DISPLAY_INFO (f)->color_names; cache_entry; 2407 for (cache_entry = FRAME_DISPLAY_INFO (f)->color_names; cache_entry;
2431 cache_entry = cache_entry->next) 2408 cache_entry = cache_entry->next)
2432 { 2409 {