diff options
| author | Mattias EngdegÄrd | 2020-06-12 18:12:37 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2020-06-21 21:22:26 +0200 |
| commit | 9fe2bdb88a4ebd4b2286c1c2a2a2ba7411af01b6 (patch) | |
| tree | 0979ec4f38172e25a0420eca5b22650c249a80f4 | |
| parent | 0792f8e4f0de2328c57d552a5845bdf77265a971 (diff) | |
| download | emacs-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.
| -rw-r--r-- | lisp/term/tty-colors.el | 58 | ||||
| -rw-r--r-- | src/dispextern.h | 2 | ||||
| -rw-r--r-- | src/nsterm.m | 59 | ||||
| -rw-r--r-- | src/w32fns.c | 157 | ||||
| -rw-r--r-- | src/xfaces.c | 116 | ||||
| -rw-r--r-- | src/xterm.c | 51 | ||||
| -rw-r--r-- | test/src/xfaces-tests.el | 23 |
7 files changed, 180 insertions, 286 deletions
diff --git a/lisp/term/tty-colors.el b/lisp/term/tty-colors.el index 39ca2d36276..73e2431822e 100644 --- a/lisp/term/tty-colors.el +++ b/lisp/term/tty-colors.el | |||
| @@ -923,62 +923,8 @@ The returned value reflects the standard Emacs definition of | |||
| 923 | COLOR (see the info node `(emacs) Colors'), regardless of whether | 923 | COLOR (see the info node `(emacs) Colors'), regardless of whether |
| 924 | the terminal can display it, so the return value should be the | 924 | the terminal can display it, so the return value should be the |
| 925 | same regardless of what display is being used." | 925 | same regardless of what display is being used." |
| 926 | (let ((len (length color))) | 926 | (or (internal-color-values-from-color-spec color) |
| 927 | (cond ((and (>= len 4) ;; HTML/CSS/SVG-style "#XXYYZZ" color spec | 927 | (cdr (assoc color color-name-rgb-alist)))) |
| 928 | (eq (aref color 0) ?#) | ||
| 929 | (member (aref color 1) | ||
| 930 | '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 | ||
| 931 | ?a ?b ?c ?d ?e ?f | ||
| 932 | ?A ?B ?C ?D ?E ?F))) | ||
| 933 | ;; Translate the string "#XXYYZZ" into a list of numbers | ||
| 934 | ;; (XX YY ZZ), scaling each to the {0..65535} range. This | ||
| 935 | ;; follows the HTML color convention, where both "#fff" and | ||
| 936 | ;; "#ffffff" represent the same color, white. | ||
| 937 | (let* ((ndig (/ (- len 1) 3)) | ||
| 938 | (maxval (1- (ash 1 (* 4 ndig)))) | ||
| 939 | (i1 1) | ||
| 940 | (i2 (+ i1 ndig)) | ||
| 941 | (i3 (+ i2 ndig)) | ||
| 942 | (i4 (+ i3 ndig))) | ||
| 943 | (list | ||
| 944 | (/ (* (string-to-number | ||
| 945 | (substring color i1 i2) 16) | ||
| 946 | 65535) | ||
| 947 | maxval) | ||
| 948 | (/ (* (string-to-number | ||
| 949 | (substring color i2 i3) 16) | ||
| 950 | 65535) | ||
| 951 | maxval) | ||
| 952 | (/ (* (string-to-number | ||
| 953 | (substring color i3 i4) 16) | ||
| 954 | 65535) | ||
| 955 | maxval)))) | ||
| 956 | ((and (>= len 9) ;; X-style rgb:xx/yy/zz color spec | ||
| 957 | (string= (substring color 0 4) "rgb:")) | ||
| 958 | ;; Translate the string "rgb:XX/YY/ZZ" into a list of | ||
| 959 | ;; numbers (XX YY ZZ), scaling each to the {0..65535} | ||
| 960 | ;; range. "rgb:F/F/F" is white. | ||
| 961 | (let* ((ndig (/ (- len 3) 3)) | ||
| 962 | (maxval (1- (ash 1 (* 4 (- ndig 1))))) | ||
| 963 | (i1 4) | ||
| 964 | (i2 (+ i1 ndig)) | ||
| 965 | (i3 (+ i2 ndig)) | ||
| 966 | (i4 (+ i3 ndig))) | ||
| 967 | (list | ||
| 968 | (/ (* (string-to-number | ||
| 969 | (substring color i1 (- i2 1)) 16) | ||
| 970 | 65535) | ||
| 971 | maxval) | ||
| 972 | (/ (* (string-to-number | ||
| 973 | (substring color i2 (- i3 1)) 16) | ||
| 974 | 65535) | ||
| 975 | maxval) | ||
| 976 | (/ (* (string-to-number | ||
| 977 | (substring color i3 (1- i4)) 16) | ||
| 978 | 65535) | ||
| 979 | maxval)))) | ||
| 980 | (t | ||
| 981 | (cdr (assoc color color-name-rgb-alist)))))) | ||
| 982 | 928 | ||
| 983 | (defun tty-color-translate (color &optional frame) | 929 | (defun tty-color-translate (color &optional frame) |
| 984 | "Given a color COLOR, return the index of the corresponding TTY color. | 930 | "Given a color COLOR, return the index of the corresponding TTY color. |
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); |
| 3515 | extern bool tty_defined_color (struct frame *, const char *, Emacs_Color *, | 3515 | extern bool tty_defined_color (struct frame *, const char *, Emacs_Color *, |
| 3516 | bool, bool); | 3516 | bool, bool); |
| 3517 | bool parse_color_spec (const char *, | ||
| 3518 | unsigned short *, unsigned short *, unsigned short *); | ||
| 3517 | 3519 | ||
| 3518 | Lisp_Object tty_color_name (struct frame *, int); | 3520 | Lisp_Object tty_color_name (struct frame *, int); |
| 3519 | void clear_face_cache (bool); | 3521 | void 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. */ | ||
| 827 | static bool | ||
| 828 | parse_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. */ | ||
| 854 | static double | ||
| 855 | parse_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. */ | ||
| 871 | bool | ||
| 872 | parse_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 | |||
| 914 | DEFUN ("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). | ||
| 919 | Recognised 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 | |||
| 925 | The result is normalized to a maximum value of 65535 per component, | ||
| 926 | forming a list of three integers in [0,65535]. | ||
| 927 | If 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) | |||
| 2389 | Status x_parse_color (struct frame *f, const char *color_name, | 2387 | Status 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 | { |
diff --git a/test/src/xfaces-tests.el b/test/src/xfaces-tests.el index 5ed16c9e51d..34cda07e5b4 100644 --- a/test/src/xfaces-tests.el +++ b/test/src/xfaces-tests.el | |||
| @@ -24,4 +24,27 @@ | |||
| 24 | (should (equal (color-distance "#222222" "#ffffff") | 24 | (should (equal (color-distance "#222222" "#ffffff") |
| 25 | (color-distance "#ffffff" "#222222")))) | 25 | (color-distance "#ffffff" "#222222")))) |
| 26 | 26 | ||
| 27 | (ert-deftest xfaces-internal-color-values-from-color-spec () | ||
| 28 | (should (equal (internal-color-values-from-color-spec "#f05") | ||
| 29 | '(#xffff #x0000 #x5555))) | ||
| 30 | (should (equal (internal-color-values-from-color-spec "#1fb0C5") | ||
| 31 | '(#x1f1f #xb0b0 #xc5c5))) | ||
| 32 | (should (equal (internal-color-values-from-color-spec "#1f8b0AC5e") | ||
| 33 | '(#x1f81 #xb0aa #xc5eb))) | ||
| 34 | (should (equal (internal-color-values-from-color-spec "#1f83b0ADC5e2") | ||
| 35 | '(#x1f83 #xb0ad #xc5e2))) | ||
| 36 | (should (equal (internal-color-values-from-color-spec "#1f83b0ADC5e2g") nil)) | ||
| 37 | (should (equal (internal-color-values-from-color-spec "#1f83b0ADC5e20") nil)) | ||
| 38 | (should (equal (internal-color-values-from-color-spec "#12345") nil)) | ||
| 39 | (should (equal (internal-color-values-from-color-spec "rgb:f/23/28a") | ||
| 40 | '(#xffff #x2323 #x28a2))) | ||
| 41 | (should (equal (internal-color-values-from-color-spec "rgb:1234/5678/09ab") | ||
| 42 | '(#x1234 #x5678 #x09ab))) | ||
| 43 | (should (equal (internal-color-values-from-color-spec "rgb:0//0") nil)) | ||
| 44 | (should (equal (internal-color-values-from-color-spec "rgbi:0/0.5/0.1") | ||
| 45 | '(0 32768 6554))) | ||
| 46 | (should (equal (internal-color-values-from-color-spec "rgbi:1e-3/1.0e-2/1e0") | ||
| 47 | '(66 655 65535))) | ||
| 48 | (should (equal (internal-color-values-from-color-spec "rgbi:0/0.5/10") nil))) | ||
| 49 | |||
| 27 | (provide 'xfaces-tests) | 50 | (provide 'xfaces-tests) |