Merge pull request #40868 from aaronfranke/color-html-rgba
Change Color HTML conversion from ARGB to RGBA
This commit is contained in:
commit
eac32f082a
128
core/color.cpp
128
core/color.cpp
|
@ -261,33 +261,21 @@ Color Color::from_rgbe9995(uint32_t p_rgbe) {
|
|||
return Color(rd, gd, bd, 1.0f);
|
||||
}
|
||||
|
||||
static float _parse_col(const String &p_str, int p_ofs) {
|
||||
int ig = 0;
|
||||
static int _parse_col4(const String &p_str, int p_ofs) {
|
||||
char character = p_str[p_ofs];
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int c = p_str[i + p_ofs];
|
||||
int v = 0;
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
v = c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
v = c - 'a';
|
||||
v += 10;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
v = c - 'A';
|
||||
v += 10;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
ig += v * 16;
|
||||
} else {
|
||||
ig += v;
|
||||
}
|
||||
if (character >= '0' && character <= '9') {
|
||||
return character - '0';
|
||||
} else if (character >= 'a' && character <= 'f') {
|
||||
return character + (10 - 'a');
|
||||
} else if (character >= 'A' && character <= 'F') {
|
||||
return character + (10 - 'A');
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ig;
|
||||
static int _parse_col8(const String &p_str, int p_ofs) {
|
||||
return _parse_col4(p_str, p_ofs) * 16 + _parse_col4(p_str, p_ofs + 1);
|
||||
}
|
||||
|
||||
Color Color::inverted() const {
|
||||
|
@ -302,49 +290,54 @@ Color Color::contrasted() const {
|
|||
return c;
|
||||
}
|
||||
|
||||
Color Color::html(const String &p_color) {
|
||||
String color = p_color;
|
||||
Color Color::html(const String &p_rgba) {
|
||||
String color = p_rgba;
|
||||
if (color.length() == 0) {
|
||||
return Color();
|
||||
}
|
||||
if (color[0] == '#') {
|
||||
color = color.substr(1, color.length() - 1);
|
||||
}
|
||||
if (color.length() == 3 || color.length() == 4) {
|
||||
String exp_color;
|
||||
for (int i = 0; i < color.length(); i++) {
|
||||
exp_color += color[i];
|
||||
exp_color += color[i];
|
||||
}
|
||||
color = exp_color;
|
||||
color = color.substr(1);
|
||||
}
|
||||
|
||||
// If enabled, use 1 hex digit per channel instead of 2.
|
||||
// Other sizes aren't in the HTML/CSS spec but we could add them if desired.
|
||||
bool is_shorthand = color.length() < 5;
|
||||
bool alpha = false;
|
||||
|
||||
if (color.length() == 8) {
|
||||
alpha = true;
|
||||
} else if (color.length() == 6) {
|
||||
alpha = false;
|
||||
} else if (color.length() == 4) {
|
||||
alpha = true;
|
||||
} else if (color.length() == 3) {
|
||||
alpha = false;
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + ".");
|
||||
ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_rgba + ".");
|
||||
}
|
||||
|
||||
int a = 255;
|
||||
if (alpha) {
|
||||
a = _parse_col(color, 0);
|
||||
ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
float r, g, b, a = 1.0;
|
||||
if (is_shorthand) {
|
||||
r = _parse_col4(color, 0) / 15.0;
|
||||
g = _parse_col4(color, 1) / 15.0;
|
||||
b = _parse_col4(color, 2) / 15.0;
|
||||
if (alpha) {
|
||||
a = _parse_col4(color, 3) / 15.0;
|
||||
}
|
||||
} else {
|
||||
r = _parse_col8(color, 0) / 255.0;
|
||||
g = _parse_col8(color, 2) / 255.0;
|
||||
b = _parse_col8(color, 4) / 255.0;
|
||||
if (alpha) {
|
||||
a = _parse_col8(color, 6) / 255.0;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_rgba + ".");
|
||||
ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_rgba + ".");
|
||||
ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_rgba + ".");
|
||||
ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_rgba + ".");
|
||||
|
||||
int from = alpha ? 2 : 0;
|
||||
|
||||
int r = _parse_col(color, from + 0);
|
||||
ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
int g = _parse_col(color, from + 2);
|
||||
ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
int b = _parse_col(color, from + 4);
|
||||
ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
|
||||
return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
|
||||
return Color(r, g, b, a);
|
||||
}
|
||||
|
||||
bool Color::html_is_valid(const String &p_color) {
|
||||
|
@ -354,41 +347,22 @@ bool Color::html_is_valid(const String &p_color) {
|
|||
return false;
|
||||
}
|
||||
if (color[0] == '#') {
|
||||
color = color.substr(1, color.length() - 1);
|
||||
color = color.substr(1);
|
||||
}
|
||||
|
||||
bool alpha = false;
|
||||
|
||||
if (color.length() == 8) {
|
||||
alpha = true;
|
||||
} else if (color.length() == 6) {
|
||||
alpha = false;
|
||||
} else {
|
||||
// Check if the amount of hex digits is valid.
|
||||
int len = color.length();
|
||||
if (!(len == 3 || len == 4 || len == 6 || len == 8)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (alpha) {
|
||||
int a = _parse_col(color, 0);
|
||||
if (a < 0) {
|
||||
// Check if each hex digit is valid.
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (_parse_col4(color, i) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int from = alpha ? 2 : 0;
|
||||
|
||||
int r = _parse_col(color, from + 0);
|
||||
if (r < 0) {
|
||||
return false;
|
||||
}
|
||||
int g = _parse_col(color, from + 2);
|
||||
if (g < 0) {
|
||||
return false;
|
||||
}
|
||||
int b = _parse_col(color, from + 4);
|
||||
if (b < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -438,7 +412,7 @@ String Color::to_html(bool p_alpha) const {
|
|||
txt += _to_hex(g);
|
||||
txt += _to_hex(b);
|
||||
if (p_alpha) {
|
||||
txt = _to_hex(a) + txt;
|
||||
txt += _to_hex(a);
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ struct Color {
|
|||
|
||||
static Color hex(uint32_t p_hex);
|
||||
static Color hex64(uint64_t p_hex);
|
||||
static Color html(const String &p_color);
|
||||
static Color html(const String &p_rgba);
|
||||
static bool html_is_valid(const String &p_color);
|
||||
static Color named(const String &p_name);
|
||||
String to_html(bool p_alpha = true) const;
|
||||
|
|
|
@ -18,13 +18,21 @@
|
|||
<argument index="0" name="from" type="String">
|
||||
</argument>
|
||||
<description>
|
||||
Constructs a color from an HTML hexadecimal color string in ARGB or RGB format. See also [method @GDScript.ColorN].
|
||||
Constructs a color from an HTML hexadecimal color string in RGB or RGBA format. See also [method @GDScript.ColorN].
|
||||
[codeblock]
|
||||
# Each of the following creates the same color RGBA(178, 217, 10, 255).
|
||||
var c1 = Color("#ffb2d90a") # ARGB format with "#".
|
||||
var c2 = Color("ffb2d90a") # ARGB format.
|
||||
var c3 = Color("#b2d90a") # RGB format with "#".
|
||||
var c4 = Color("b2d90a") # RGB format.
|
||||
var c1 = Color("#b2d90aff") # RGBA format with "#".
|
||||
var c2 = Color("b2d90aff") # RGBA format.
|
||||
[/codeblock]
|
||||
You can also use the "web color" short-hand form by only using 3 or 4 digits.
|
||||
[codeblock]
|
||||
# Each of the following creates the same color RGBA(17, 34, 51, 255).
|
||||
var c3 = Color("#123") # RGB format with "#".
|
||||
var c4 = Color("123") # RGB format.
|
||||
var c1 = Color("#123f") # RGBA format with "#".
|
||||
var c2 = Color("123f") # RGBA format.
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
|
@ -243,11 +251,11 @@
|
|||
<argument index="0" name="with_alpha" type="bool" default="true">
|
||||
</argument>
|
||||
<description>
|
||||
Returns the color's HTML hexadecimal color string in ARGB format (ex: [code]ff34f822[/code]).
|
||||
Setting [code]with_alpha[/code] to [code]false[/code] excludes alpha from the hexadecimal string.
|
||||
Returns the color's HTML hexadecimal color string in RGBA format (ex: [code]ff34f822[/code]).
|
||||
Setting [code]with_alpha[/code] to [code]false[/code] excludes alpha from the hexadecimal string (and uses RGB instead of RGBA format).
|
||||
[codeblock]
|
||||
var c = Color(1, 1, 1, 0.5)
|
||||
var s1 = c.to_html() # Returns "7fffffff"
|
||||
var s1 = c.to_html() # Returns "ffffff7f"
|
||||
var s2 = c.to_html(false) # Returns "ffffff"
|
||||
[/codeblock]
|
||||
</description>
|
||||
|
|
|
@ -206,8 +206,8 @@ void ThemeEditor::_save_template_cbk(String fname) {
|
|||
file->store_line("; [value] examples:");
|
||||
file->store_line("; ");
|
||||
file->store_line("; Type.item = 6 ; numeric constant. ");
|
||||
file->store_line("; Type.item = #FF00FF ; HTML color ");
|
||||
file->store_line("; Type.item = #55FF00FF ; HTML color with alpha 55.");
|
||||
file->store_line("; Type.item = #FF00FF ; HTML color (magenta).");
|
||||
file->store_line("; Type.item = #FF00FF55 ; HTML color (magenta with alpha 0x55).");
|
||||
file->store_line("; Type.item = icon(image.png) ; icon in a png file (relative to theme file).");
|
||||
file->store_line("; Type.item = font(font.xres) ; font in a resource (relative to theme file).");
|
||||
file->store_line("; Type.item = sbox(stylebox.xres) ; stylebox in a resource (relative to theme file).");
|
||||
|
|
|
@ -565,6 +565,9 @@ namespace Godot
|
|||
rgba = rgba.Substring(1);
|
||||
}
|
||||
|
||||
// If enabled, use 1 hex digit per channel instead of 2.
|
||||
// Other sizes aren't in the HTML/CSS spec but we could add them if desired.
|
||||
bool isShorthand = rgba.Length < 5;
|
||||
bool alpha;
|
||||
|
||||
if (rgba.Length == 8)
|
||||
|
@ -575,47 +578,60 @@ namespace Godot
|
|||
{
|
||||
alpha = false;
|
||||
}
|
||||
else if (rgba.Length == 4)
|
||||
{
|
||||
alpha = true;
|
||||
}
|
||||
else if (rgba.Length == 3)
|
||||
{
|
||||
alpha = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
|
||||
}
|
||||
|
||||
if (alpha)
|
||||
a = 1.0f;
|
||||
if (isShorthand)
|
||||
{
|
||||
a = ParseCol8(rgba, 6) / 255f;
|
||||
|
||||
if (a < 0)
|
||||
r = ParseCol4(rgba, 0) / 15f;
|
||||
g = ParseCol4(rgba, 1) / 15f;
|
||||
b = ParseCol4(rgba, 2) / 15f;
|
||||
if (alpha)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
|
||||
a = ParseCol4(rgba, 3) / 15f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a = 1.0f;
|
||||
r = ParseCol8(rgba, 0) / 255f;
|
||||
g = ParseCol8(rgba, 2) / 255f;
|
||||
b = ParseCol8(rgba, 4) / 255f;
|
||||
if (alpha)
|
||||
{
|
||||
a = ParseCol8(rgba, 6) / 255f;
|
||||
}
|
||||
}
|
||||
|
||||
int from = alpha ? 2 : 0;
|
||||
|
||||
r = ParseCol8(rgba, 0) / 255f;
|
||||
|
||||
if (r < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
|
||||
}
|
||||
|
||||
g = ParseCol8(rgba, 2) / 255f;
|
||||
|
||||
if (g < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
|
||||
}
|
||||
|
||||
b = ParseCol8(rgba, 4) / 255f;
|
||||
|
||||
if (b < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
|
||||
}
|
||||
|
||||
if (a < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -751,45 +767,28 @@ namespace Godot
|
|||
value = max;
|
||||
}
|
||||
|
||||
private static int ParseCol4(string str, int ofs)
|
||||
{
|
||||
char character = str[ofs];
|
||||
|
||||
if (character >= '0' && character <= '9')
|
||||
{
|
||||
return character - '0';
|
||||
}
|
||||
else if (character >= 'a' && character <= 'f')
|
||||
{
|
||||
return character + (10 - 'a');
|
||||
}
|
||||
else if (character >= 'A' && character <= 'F')
|
||||
{
|
||||
return character + (10 - 'A');
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int ParseCol8(string str, int ofs)
|
||||
{
|
||||
int ig = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int c = str[i + ofs];
|
||||
int v;
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
v = c - '0';
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
v = c - 'a';
|
||||
v += 10;
|
||||
}
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
v = c - 'A';
|
||||
v += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
ig += v * 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
ig += v;
|
||||
}
|
||||
}
|
||||
|
||||
return ig;
|
||||
return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
|
||||
}
|
||||
|
||||
private String ToHex32(float val)
|
||||
|
@ -828,46 +827,24 @@ namespace Godot
|
|||
|
||||
if (color[0] == '#')
|
||||
{
|
||||
color = color.Substring(1, color.Length - 1);
|
||||
color = color.Substring(1);
|
||||
}
|
||||
|
||||
bool alpha;
|
||||
|
||||
switch (color.Length)
|
||||
// Check if the amount of hex digits is valid.
|
||||
int len = color.Length;
|
||||
if (!(len == 3 || len == 4 || len == 6 || len == 8))
|
||||
{
|
||||
case 8:
|
||||
alpha = true;
|
||||
break;
|
||||
case 6:
|
||||
alpha = false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (alpha)
|
||||
{
|
||||
if (ParseCol8(color, 0) < 0)
|
||||
// Check if each hex digit is valid.
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (ParseCol4(color, i) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int from = alpha ? 2 : 0;
|
||||
|
||||
if (ParseCol8(color, from + 0) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ParseCol8(color, from + 2) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ParseCol8(color, from + 4) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,9 +39,7 @@ namespace TestColor {
|
|||
|
||||
TEST_CASE("[Color] Constructor methods") {
|
||||
const Color blue_rgba = Color(0.25098, 0.376471, 1, 0.501961);
|
||||
// HTML currently uses ARGB notation, which is contrary to the CSS standard.
|
||||
// This may be changed to RGBA in 4.0.
|
||||
const Color blue_html = Color::html("#804060ff");
|
||||
const Color blue_html = Color::html("#4060ff80");
|
||||
const Color blue_hex = Color::hex(0x4060ff80);
|
||||
const Color blue_hex64 = Color::hex64(0x4040'6060'ffff'8080);
|
||||
|
||||
|
@ -118,10 +116,10 @@ TEST_CASE("[Color] Conversion methods") {
|
|||
const Color cyan_transparent = Color(0, 1, 1, 0);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
cyan.to_html() == "ff00ffff",
|
||||
cyan.to_html() == "00ffffff",
|
||||
"The returned RGB HTML color code should match the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
cyan_transparent.to_html() == "0000ffff",
|
||||
cyan_transparent.to_html() == "00ffff00",
|
||||
"The returned RGBA HTML color code should match the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
cyan.to_argb32() == 0xff00ffff,
|
||||
|
|
Loading…
Reference in New Issue