changeset 29127:5b8885af4e2f

Allow color graphic properties to accept HTML specifications (bug #59562). * NEWS: Announce new feature. * plot.txi: Document new feature under colorspec in manual. * graphics.cc (color_values::str2rgb): Find strings that start with '#' and use std::stoi to try and convert from hexadecimal to base10. If conversion fails, return false from function which will later cause an error to be thrown about incorrect input.
author Rik <rik@octave.org>
date Mon, 30 Nov 2020 13:33:27 -0800
parents 54509b7fddd3
children 51bb0e599af4
files NEWS doc/interpreter/plot.txi libinterp/corefcn/graphics.cc
diffstat 3 files changed, 78 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Thu Nov 12 14:38:38 2020 +0100
+++ b/NEWS	Mon Nov 30 13:33:27 2020 -0800
@@ -135,9 +135,15 @@
 
 - The `FMT` format argument for plot commands now accepts long forms for
 color names which may be more understandable than the existing
-one-letter codes.   For example, the RGB value `[0 0 0]` can now be
+one-letter codes.  For example, the RGB value `[0 0 0]` can now be
 specified by `"black"` in addition to `"k"`.
 
+- The color graphics properties, for example `"EdgeColor"` or
+`"FaceColor"`, now accept HTML specifications.  An HTML specification is
+a string that begins with the character '#' and is followed by either 3
+or 6 hexadecimal digits.  For example, magenta which is 100% red and
+blue values can specified by `"#FF00FF"` or `"#F0F"`.  
+
 - `uicontrol` objects now fully implement the `"Off"` and `"Inactive"`
 values of the `"Enable"` property.  When the value is `"Off"`, no
 interaction with the object occurs and the `uicontrol` changes color
--- a/doc/interpreter/plot.txi	Thu Nov 12 14:38:38 2020 +0100
+++ b/doc/interpreter/plot.txi	Mon Nov 30 13:33:27 2020 -0800
@@ -1830,10 +1830,44 @@
 @cindex graphics colors
 @cindex colors, graphics
 
-Colors may be specified as RGB triplets with values ranging from zero to
-one, or by name.  Recognized color names include @qcode{"blue"},
-@qcode{"black"}, @qcode{"cyan"}, @qcode{"green"}, @qcode{"magenta"},
-@qcode{"red"}, @qcode{"white"}, and @qcode{"yellow"}.
+Colors may be specified in three ways: 1) RGB triplets, 2) by name, or 3) by HTML notation.
+
+@table @asis
+
+@item RGB triplet
+
+An RGB triplet is a 1x3 vector where each value is between 0 and 1 inclusive.
+The first value represents the percentage of Red, the second value the
+percentage of Green, and the third value the percentage of Blue.  For example,
+@code{[1, 0, 1]} represents full Red and Blue channels resulting in the color
+magenta.
+
+@item short or long name
+
+Eight colors can be specified directly by name or by a single character short
+name.
+
+@multitable @columnfractions 0.21 0.79
+@headitem Name @tab Color
+@item @samp{k}, @qcode{"black"}   @tab blacK
+@item @samp{r}, @qcode{"red"}     @tab Red
+@item @samp{g}, @qcode{"green"}   @tab Green
+@item @samp{b}, @qcode{"blue"}    @tab Blue
+@item @samp{y}, @qcode{"yellow"}  @tab Yellow
+@item @samp{m}, @qcode{"magenta"} @tab Magenta
+@item @samp{c}, @qcode{"cyan"}    @tab Cyan
+@item @samp{w}, @qcode{"white"}   @tab White
+@end multitable
+
+@item HTML notation
+
+HTML notation is a string that begins with the character @samp{#} and is
+followed by either 3 or 6 hexadecimal digits.  As with RGB triplets, each
+hexadecimal number represents the fraction of the Red, Green, and Blue channels
+present in the specified color.  For example, @qcode{"#FF00FF"} represents
+the color magenta.
+
+@end table
 
 @node Line Styles
 @subsection Line Styles
--- a/libinterp/corefcn/graphics.cc	Thu Nov 12 14:38:38 2020 +0100
+++ b/libinterp/corefcn/graphics.cc	Mon Nov 30 13:33:27 2020 -0800
@@ -1381,6 +1381,7 @@
 
   std::transform (str.begin (), str.end (), str.begin (), tolower);
 
+  // "blue" must precede black for Matlab compatibility
   if (str.compare (0, len, "blue", 0, len) == 0)
     tmp_rgb[2] = 1;
   else if (str.compare (0, len, "black", 0, len) == 0
@@ -1399,6 +1400,38 @@
   else if (str.compare (0, len, "white", 0, len) == 0
            || str.compare (0, len, "w", 0, len) == 0)
     tmp_rgb[0] = tmp_rgb[1] = tmp_rgb[2] = 1;
+  else if (str[0] == '#' && len == 7)
+    {
+      try
+        {
+          tmp_rgb[0] = static_cast<double> (stoi (str.substr (1,2), nullptr, 16))
+                       / 255.0;
+          tmp_rgb[1] = static_cast<double> (stoi (str.substr (3,2), nullptr, 16))
+                       / 255.0;
+          tmp_rgb[2] = static_cast<double> (stoi (str.substr (5,2), nullptr, 16))
+                       / 255.0;
+        }
+      catch (...)
+        {
+          retval = false;
+        }
+    }
+  else if (str[0] == '#' && len == 4)
+    {
+      try
+        {
+          tmp_rgb[0] = static_cast<double> (stoi (str.substr (1,1), nullptr, 16))
+                       / 15.0;
+          tmp_rgb[1] = static_cast<double> (stoi (str.substr (2,1), nullptr, 16))
+                       / 15.0;
+          tmp_rgb[2] = static_cast<double> (stoi (str.substr (3,1), nullptr, 16))
+                       / 15.0;
+        }
+      catch (...)
+        {
+          retval = false;
+        }
+    }
   else
     retval = false;