Use the CSS convention for #RGB colors (bug#36304)

* src/xterm.c (x_parse_color): Change interpretation of #RGB color
triplets to match CSS rather than X conventions.

* lisp/term/tty-colors.el (tty-color-standard-values): Change
interpretation of #RGB color triplets to match CSS rather than X
conventions.  Allow upper-case digits.  Fix rgb:R/G/B
interpretation.

* doc/emacs/display.texi (Colors): Specify the convention used for
"#RGB" color triplets.

* test/lisp/tty-colors-tests.el: New file.

* etc/NEWS: Mention the change.
This commit is contained in:
Pip Cet 2019-07-22 02:40:35 +00:00 committed by Eli Zaretskii
parent e310843d9d
commit 357399014a
4 changed files with 88 additions and 44 deletions

View file

@ -556,14 +556,14 @@ Font Lock mode.
@node Colors
@section Colors for Faces
@cindex color name
@cindex RGB triplet
Faces can have various foreground and background colors. When you
specify a color for a face---for instance, when customizing the face
(@pxref{Face Customization})---you can use either a @dfn{color name}
or an @dfn{RGB triplet}.
@subsection Color Names
@cindex color name
@findex list-colors-display
@vindex list-colors-sort
A color name is a pre-defined name, such as @samp{dark orange} or
@ -578,12 +578,16 @@ such terminals. However, Emacs understands X11 color names even on
text terminals; if a face is given a color specified by an X11 color
name, it is displayed using the closest-matching terminal color.
@subsection RGB Triplets
@cindex RGB triplet
An RGB triplet is a string of the form @samp{#RRGGBB}. Each of the
R, G, and B components is a hexadecimal number specifying the
component's relative intensity, one to four digits long (usually two
digits are used). The components must have the same number of digits.
For hexadecimal values A to F, either upper or lower case are
acceptable.
primary color components is represented by a hexadecimal number
between @samp{00} (intensity 0) and @samp{FF} (the maximum intensity).
It is also possible to use one, three, or four hex digits for each
component, so @samp{red} can be represented as @samp{#F00},
@samp{#fff000000}, or @samp{#ffff00000000}. The components must have
the same number of digits. For hexadecimal values A to F, either
upper or lower case are acceptable.
The @kbd{M-x list-colors-display} command also shows the equivalent
RGB triplet for each named color. For instance, @samp{medium sea

View file

@ -407,6 +407,12 @@ names in xref buffers.
** New variable `file-size-function' controls how file sizes are displayed.
+++
** Emacs now interprets RGB triplets like HTML, SVG, and CSS do.
The X convention previously used differed slightly, particularly for
RGB triplets with a single hexadecimal digit per component.
* Editing Changes in Emacs 27.1
@ -1509,7 +1515,6 @@ automatically updates. In the buffer, you can use 's q' or 's e' to
signal a thread with quit or error respectively, or get a snapshot
backtrace with 'b'.
** thingatpt.el
---

View file

@ -919,57 +919,63 @@ FRAME defaults to the selected frame."
The result is a list of integer RGB values--(RED GREEN BLUE).
These values range from 0 to 65535; white is (65535 65535 65535).
The returned value reflects the standard X definition of COLOR,
regardless of whether the terminal can display it, so the return value
should be the same regardless of what display is being used."
The returned value reflects the standard Emacs definition of
COLOR (see the info node `(emacs) Colors'), regardless of whether
the terminal can display it, so the return value should be the
same regardless of what display is being used."
(let ((len (length color)))
(cond ((and (>= len 4) ;; X-style "#XXYYZZ" color spec
(cond ((and (>= len 4) ;; HTML/CSS/SVG-style "#XXYYZZ" color spec
(eq (aref color 0) ?#)
(member (aref color 1)
'(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
?a ?b ?c ?d ?e ?f)))
;; Translate the string "#XXYYZZ" into a list
;; of numbers (XX YY ZZ). If the primary colors
;; are specified with less than 4 hex digits,
;; the used digits represent the most significant
;; bits of the value (e.g. #XYZ = #X000Y000Z000).
?a ?b ?c ?d ?e ?f
?A ?B ?C ?D ?E ?F)))
;; Translate the string "#XXYYZZ" into a list of numbers
;; (XX YY ZZ), scaling each to the {0..65535} range. This
;; follows the HTML color convention, where both "#fff" and
;; "#ffffff" represent the same color, white.
(let* ((ndig (/ (- len 1) 3))
(maxval (1- (ash 1 (* 4 ndig))))
(i1 1)
(i2 (+ i1 ndig))
(i3 (+ i2 ndig)))
(i3 (+ i2 ndig))
(i4 (+ i3 ndig)))
(list
(ash
(string-to-number (substring color i1 i2) 16)
(* 4 (- 4 ndig)))
(ash
(string-to-number (substring color i2 i3) 16)
(* 4 (- 4 ndig)))
(ash
(string-to-number (substring color i3) 16)
(* 4 (- 4 ndig))))))
((and (>= len 9) ;; X-style RGB:xx/yy/zz color spec
(/ (* (string-to-number
(substring color i1 i2) 16)
65535)
maxval)
(/ (* (string-to-number
(substring color i2 i3) 16)
65535)
maxval)
(/ (* (string-to-number
(substring color i3 i4) 16)
65535)
maxval))))
((and (>= len 9) ;; X-style rgb:xx/yy/zz color spec
(string= (substring color 0 4) "rgb:"))
;; Translate the string "RGB:XX/YY/ZZ" into a list
;; of numbers (XX YY ZZ). If fewer than 4 hex
;; digits are used, they represent the fraction
;; of the maximum value (RGB:X/Y/Z = #XXXXYYYYZZZZ).
;; Translate the string "rgb:XX/YY/ZZ" into a list of
;; numbers (XX YY ZZ), scaling each to the {0..65535}
;; range. "rgb:F/F/F" is white.
(let* ((ndig (/ (- len 3) 3))
(maxval (1- (ash 1 (* 4 (- ndig 1)))))
(i1 4)
(i2 (+ i1 ndig))
(i3 (+ i2 ndig)))
(i3 (+ i2 ndig))
(i4 (+ i3 ndig)))
(list
(/ (* (string-to-number
(substring color i1 (- i2 1)) 16)
255)
65535)
maxval)
(/ (* (string-to-number
(substring color i2 (- i3 1)) 16)
255)
65535)
maxval)
(/ (* (string-to-number
(substring color i3) 16)
255)
(substring color i3 (1- i4)) 16)
65535)
maxval))))
(t
(cdr (assoc color color-name-rgb-alist))))))
@ -977,9 +983,9 @@ should be the same regardless of what display is being used."
(defun tty-color-translate (color &optional frame)
"Given a color COLOR, return the index of the corresponding TTY color.
COLOR must be a string that is either the color's name, or its X-style
specification like \"#RRGGBB\" or \"RGB:rr/gg/bb\", where each primary.
color can be given with 1 to 4 hex digits.
COLOR must be a string that is either the color's name, or its
color triplet specification like \"#RRGGBB\" or \"rgb:RR/GG/BB\",
where each primary color can be given with 1 to 4 hex digits.
If COLOR is a color name that is found among supported colors in
`tty-color-alist', the associated index is returned. Otherwise, the
@ -987,7 +993,7 @@ RGB values of the color, either as given by the argument or from
looking up the name in `color-name-rgb-alist', are used to find the
supported color that is the best approximation for COLOR in the RGB
space.
If COLOR is neither a valid X RGB specification of the color, nor a
If COLOR is neither a valid RGB specification of the color, nor a
name of a color in `color-name-rgb-alist', the returned value is nil.
If FRAME is unspecified or nil, it defaults to the selected frame."

View file

@ -2381,6 +2381,8 @@ x_query_frame_background_color (struct frame *f, XColor *bgcolor)
x_query_colors (f, bgcolor, 1);
}
#define HEX_COLOR_NAME_LENGTH 32
/* On frame F, translate the color name to RGB values. Use cached
information, if possible.
@ -2398,9 +2400,36 @@ Status x_parse_color (struct frame *f, const char *color_name,
if (color_name[0] == '#')
{
/* The hex form is parsed directly by XParseColor without
/* Don't pass #RGB strings directly to XParseColor, because that
follows the X convention of zero-extending each channel
value: #f00 means #f00000. We want the convention of scaling
channel values, so #f00 means #ff0000, just as it does for
HTML, SVG, and CSS.
So we translate #f00 to rgb:f/0/0, which X handles
differently. */
char rgb_color_name[HEX_COLOR_NAME_LENGTH];
int len = strlen (color_name);
int digits_per_channel;
if (len == 4)
digits_per_channel = 1;
else if (len == 7)
digits_per_channel = 2;
else if (len == 10)
digits_per_channel = 3;
else if (len == 13)
digits_per_channel = 4;
else
return 0;
snprintf (rgb_color_name, sizeof rgb_color_name, "rgb:%.*s/%.*s/%.*s",
digits_per_channel, color_name + 1,
digits_per_channel, color_name + digits_per_channel + 1,
digits_per_channel, color_name + 2 * digits_per_channel + 1);
/* The rgb form is parsed directly by XParseColor without
talking to the X server. No need for caching. */
return XParseColor (dpy, cmap, color_name, color);
return XParseColor (dpy, cmap, rgb_color_name, color);
}
for (cache_entry = FRAME_DISPLAY_INFO (f)->color_names; cache_entry;