Changed: Split css style from CGroupHTML

--HG--
branch : develop
This commit is contained in:
Nimetu 2019-03-29 15:03:39 +02:00
parent 7f41881be7
commit c107bdc56f
8 changed files with 1342 additions and 1115 deletions

View file

@ -0,0 +1,39 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CL_CSS_PARSER_H
#define CL_CSS_PARSER_H
#include "nel/misc/types_nl.h"
#include "nel/gui/css_style.h"
namespace NLGUI
{
/**
* \brief CSS style parsing
* \date 2019-03-15 10:50 GMT
* \author Meelis Mägi (Nimetu)
*/
class CCssParser {
public:
// parse style declaration, eg "color: red; font-size: 10px;"
static TStyle parseDecls(const std::string &styleString);
};
}//namespace
#endif // CL_CSS_PARSER_H

View file

@ -0,0 +1,141 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CL_CSS_STYLE_H
#define CL_CSS_STYLE_H
#include "nel/misc/types_nl.h"
#include "nel/misc/rgba.h"
namespace NLGUI
{
typedef std::map<std::string, std::string> TStyle;
/**
* \brief CSS style rules
* \date 2019-03-15 10:50 GMT
* \author Meelis Mägi (Nimetu)
*/
class CStyleParams
{
public:
struct STextShadow
{
public:
STextShadow(bool enabled = false, bool outline = false, sint32 x=1, sint32 y=1, NLMISC::CRGBA color=NLMISC::CRGBA::Black)
: Enabled(enabled), Outline(outline), X(x), Y(y), Color(color)
{ }
bool Enabled;
bool Outline;
sint32 X;
sint32 Y;
NLMISC::CRGBA Color;
};
public:
CStyleParams () : FontFamily(""), TextColor(255,255,255,255), TextShadow()
{
FontSize=10;
FontWeight=400;
FontOblique=false;
Underlined=false;
StrikeThrough=false;
GlobalColor=false;
Width=-1;
Height=-1;
MaxWidth=-1;
MaxHeight=-1;
BorderWidth=1;
BackgroundColor=NLMISC::CRGBA::Black;
BackgroundColorOver=NLMISC::CRGBA::Black;
}
uint FontSize;
uint FontWeight;
bool FontOblique;
std::string FontFamily;
NLMISC::CRGBA TextColor;
STextShadow TextShadow;
bool GlobalColor;
bool Underlined;
bool StrikeThrough;
sint32 Width;
sint32 Height;
sint32 MaxWidth;
sint32 MaxHeight;
sint32 BorderWidth;
NLMISC::CRGBA BackgroundColor;
NLMISC::CRGBA BackgroundColorOver;
};
class CCssStyle {
public:
// 'browser' style, overwriten with '<html>'
CStyleParams Root;
// current element style
CStyleParams Current;
private:
std::vector<CStyleParams> _StyleStack;
// test if str is one of "thin/medium/thick" and return its pixel value
bool scanCssLength(const std::string& str, uint32 &px) const;
// read style attribute
void getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current) const;
public:
void reset();
inline uint getFontSizeSmaller() const
{
if (Current.FontSize < 5)
return 3;
return Current.FontSize-2;
}
inline void pushStyle()
{
_StyleStack.push_back(Current);
}
inline void popStyle()
{
if (_StyleStack.empty())
Current = Root;
else
{
Current = _StyleStack.back();
_StyleStack.pop_back();
}
}
// apply style string to this.Root
void applyRootStyle(const std::string &styleString);
// apply style string to this.Current
void applyStyle(const std::string &styleString);
void applyCssMinMax(sint32 &width, sint32 &height, sint32 minw=0, sint32 minh=0, sint32 maxw=0, sint32 maxh=0) const;
};
}//namespace
#endif // CL_CSS_STYLE_H

View file

@ -24,12 +24,11 @@
#include "nel/gui/ctrl_button.h" #include "nel/gui/ctrl_button.h"
#include "nel/gui/group_table.h" #include "nel/gui/group_table.h"
#include "nel/gui/libwww_types.h" #include "nel/gui/libwww_types.h"
#include "nel/gui/css_style.h"
// forward declaration // forward declaration
typedef void CURLM; typedef void CURLM;
typedef std::map<std::string, std::string> TStyle;
namespace NLGUI namespace NLGUI
{ {
class CCtrlButton; class CCtrlButton;
@ -76,58 +75,6 @@ namespace NLGUI
static SWebOptions options; static SWebOptions options;
// text-shadow
struct STextShadow
{
public:
STextShadow(bool enabled = false, bool outline = false, sint32 x=1, sint32 y=1, NLMISC::CRGBA color=NLMISC::CRGBA::Black)
: Enabled(enabled), Outline(outline), X(x), Y(y), Color(color)
{ }
bool Enabled;
bool Outline;
sint32 X;
sint32 Y;
NLMISC::CRGBA Color;
};
class CStyleParams
{
public:
CStyleParams () : FontFamily(""), TextColor(255,255,255,255), TextShadow()
{
FontSize=10;
FontWeight=400;
FontOblique=false;
Underlined=false;
StrikeThrough=false;
GlobalColor=false;
Width=-1;
Height=-1;
MaxWidth=-1;
MaxHeight=-1;
BorderWidth=1;
BackgroundColor=NLMISC::CRGBA::Black;
BackgroundColorOver=NLMISC::CRGBA::Black;
}
uint FontSize;
uint FontWeight;
bool FontOblique;
std::string FontFamily;
NLMISC::CRGBA TextColor;
STextShadow TextShadow;
bool GlobalColor;
bool Underlined;
bool StrikeThrough;
sint32 Width;
sint32 Height;
sint32 MaxWidth;
sint32 MaxHeight;
sint32 BorderWidth;
NLMISC::CRGBA BackgroundColor;
NLMISC::CRGBA BackgroundColorOver;
};
// ImageDownload system // ImageDownload system
enum TDataType {ImgType= 0, BnpType}; enum TDataType {ImgType= 0, BnpType};
enum TImageType {NormalImage=0, OverImage}; enum TImageType {NormalImage=0, OverImage};
@ -535,33 +482,8 @@ namespace NLGUI
// IL mode // IL mode
bool _LI; bool _LI;
// Current active style // Keep track of current element style
CStyleParams _Style; CCssStyle _Style;
// Default style
CStyleParams _StyleDefault;
// Nested style stack
std::vector<CStyleParams> _StyleParams;
inline void pushStyle()
{
_StyleParams.push_back(_Style);
}
inline void popStyle()
{
if (_StyleParams.empty())
_Style = _StyleDefault;
else
{
_Style = _StyleParams.back();
_StyleParams.pop_back();
}
}
inline uint getFontSizeSmaller() const
{
if (_Style.FontSize < 5)
return 3;
return _Style.FontSize-2;
}
// Current link // Current link
std::vector<std::string> _Link; std::vector<std::string> _Link;
@ -625,14 +547,6 @@ namespace NLGUI
return _TR.back(); return _TR.back();
} }
std::vector<STextShadow> _TextShadow;
inline STextShadow getTextShadow() const
{
if (_TextShadow.empty())
return STextShadow();
return _TextShadow.back();
}
// Forms // Forms
class CForm class CForm
{ {
@ -794,10 +708,6 @@ namespace NLGUI
typedef std::map<uint32, NLMISC::CRefPtr<CGroupHTML> > TGroupHtmlByUIDMap; typedef std::map<uint32, NLMISC::CRefPtr<CGroupHTML> > TGroupHtmlByUIDMap;
static TGroupHtmlByUIDMap _GroupHtmlByUID; static TGroupHtmlByUIDMap _GroupHtmlByUID;
// read style attribute
void getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current);
void applyCssMinMax(sint32 &width, sint32 &height, sint32 minw=0, sint32 minh=0, sint32 maxw=0, sint32 maxh=0);
// load and render local html file (from bnp for example) // load and render local html file (from bnp for example)
void doBrowseLocalFile(const std::string &filename); void doBrowseLocalFile(const std::string &filename);

View file

@ -276,9 +276,14 @@ namespace NLGUI
HTML_ATTR(H6,STYLE), HTML_ATTR(H6,STYLE),
}; };
#undef HTML_ATTR #undef HTML_ATTR
// ***************************************************************************
// Read HTML color value from src and set dest
// Can handle #rgb(a), #rrggbb(aa) or rgb()/rgba(), hsl(), hsla() formats
// or color name directly
bool scanHTMLColor(const char *src, NLMISC::CRGBA &dest);
// *************************************************************************** // ***************************************************************************
// Read a CSS length value, return true if one of supported units '%, rem, em, px, pt' // Read a CSS length value, return true if one of supported units '%, rem, em, px, pt'
// On failure: 'value' and 'unit' values are undefined // On failure: 'value' and 'unit' values are undefined

View file

@ -0,0 +1,58 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include <string>
#include "nel/misc/types_nl.h"
#include "nel/gui/css_parser.h"
#include "nel/gui/css_style.h"
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
// ***************************************************************************
// Parse style declarations style, eg. "color:red; font-size: 10px;"
//
// key is converted to lowercase
// value is left as is
TStyle CCssParser::parseDecls(const std::string &styleString)
{
TStyle styles;
std::vector<std::string> elements;
NLMISC::splitString(styleString, ";", elements);
for(uint i = 0; i < elements.size(); ++i)
{
std::string::size_type pos;
pos = elements[i].find_first_of(':');
if (pos != std::string::npos)
{
std::string key = trim(toLower(elements[i].substr(0, pos)));
std::string value = trim(elements[i].substr(pos+1));
styles[key] = value;
}
}
return styles;
}
} // namespace

View file

@ -0,0 +1,496 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include <string>
#include "nel/misc/types_nl.h"
#include "nel/gui/css_style.h"
#include "nel/gui/css_parser.h"
#include "nel/gui/libwww.h"
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
// ***************************************************************************
void CCssStyle::reset()
{
_StyleStack.clear();
Root = CStyleParams();
Current = CStyleParams();
}
// ***************************************************************************
void CCssStyle::applyRootStyle(const std::string &styleString)
{
getStyleParams(styleString, Root, Root);
}
// ***************************************************************************
void CCssStyle::applyStyle(const std::string &styleString)
{
if (_StyleStack.empty())
{
getStyleParams(styleString, Current, Root);
}
else
{
getStyleParams(styleString, Current, _StyleStack.back());
}
}
bool CCssStyle::scanCssLength(const std::string& str, uint32 &px) const
{
if (fromString(str, px))
return true;
if (str == "thin")
{
px = 1;
return true;
}
if (str == "medium")
{
px = 3;
return true;
}
if (str == "thick")
{
px = 5;
return true;
}
return false;
}
// ***************************************************************************
// CStyleParams style;
// style.FontSize; // font-size: 10px;
// style.TextColor; // color: #ABCDEF;
// style.Underlined; // text-decoration: underline; text-decoration-line: underline;
// style.StrikeThrough; // text-decoration: line-through; text-decoration-line: line-through;
void CCssStyle::getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current) const
{
float tmpf;
TStyle styles = CCssParser::parseDecls(styleString);
TStyle::iterator it;
// first pass: get font-size for 'em' sizes
for (it=styles.begin(); it != styles.end(); ++it)
{
if (it->first == "font")
{
if (it->second == "inherit")
{
style.FontSize = current.FontSize;
style.FontFamily = current.FontFamily;
style.FontWeight = current.FontWeight;
style.FontOblique = current.FontOblique;
}
}
else
if (it->first == "font-size")
{
if (it->second == "inherit")
{
style.FontSize = current.FontSize;
}
else
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.FontSize = Root.FontSize * tmpf;
else if (unit == "em")
style.FontSize = current.FontSize * tmpf;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else if (unit == "%")
style.FontSize = current.FontSize * tmpf / 100.f;
else
style.FontSize = tmpf;
}
}
}
}
// second pass: rest of style
for (it=styles.begin(); it != styles.end(); ++it)
{
if (it->first == "border")
{
sint32 b;
if (it->second == "none")
style.BorderWidth = 0;
else
if (fromString(it->second, b))
style.BorderWidth = b;
}
else
if (it->first == "font-style")
{
if (it->second == "inherit")
style.FontOblique = current.FontOblique;
else
if (it->second == "italic" || it->second == "oblique")
style.FontOblique = true;
}
else
if (it->first == "font-family")
{
if (it->second == "inherit")
style.FontFamily = current.FontFamily;
else
style.FontFamily = it->second;
}
else
if (it->first == "font-weight")
{
// https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
uint weight = 400;
if (it->second == "inherit")
weight = current.FontWeight;
else
if (it->second == "normal")
weight = 400;
else
if (it->second == "bold")
weight = 700;
else
if (it->second == "lighter")
{
const uint lighter[] = {100, 100, 100, 100, 100, 400, 400, 700, 700};
uint index = current.FontWeight / 100 - 1;
clamp(index, 1u, 9u);
weight = lighter[index-1];
}
else
if (it->second == "bolder")
{
const uint bolder[] = {400, 400, 400, 700, 700, 900, 900, 900, 900};
uint index = current.FontWeight / 100 + 1;
clamp(index, 1u, 9u);
weight = bolder[index-1];
}
else
if (fromString(it->second, weight))
{
weight = (weight / 100);
clamp(weight, 1u, 9u);
weight *= 100;
}
style.FontWeight = weight;
}
else
if (it->first == "color")
if (it->second == "inherit")
style.TextColor = current.TextColor;
else
scanHTMLColor(it->second.c_str(), style.TextColor);
else
if (it->first == "text-decoration" || it->first == "text-decoration-line")
{
std::string prop(toLower(it->second));
style.Underlined = (prop.find("underline") != std::string::npos);
style.StrikeThrough = (prop.find("line-through") != std::string::npos);
}
else
if (it->first == "text-stroke" || it->first == "-webkit-text-stroke")
{
// text-stroke: length || color
bool success = false;
uint px = 0;
CRGBA color;
std::vector<std::string> parts;
NLMISC::splitString(it->second, " ", parts);
if (parts.size() == 1)
{
success = scanCssLength(parts[0], px);
if (!success)
success = scanHTMLColor(parts[0].c_str(), color);
}
else if (parts.size() == 2)
{
success = scanCssLength(parts[0], px);
if (success)
success = scanHTMLColor(parts[1].c_str(), color);
else
{
success = scanHTMLColor(parts[0].c_str(), color);
success = success && scanCssLength(parts[1], px);
}
}
// do not disable shadow if one is already set
if (success)
{
style.TextShadow.Enabled = (px > 0);
style.TextShadow.Color = color;
style.TextShadow.X = px;
style.TextShadow.Y = px;
style.TextShadow.Outline = true;
}
}
else
if (it->first == "text-shadow")
{
if (it->second == "none")
style.TextShadow = CStyleParams::STextShadow(false);
else
if (it->second == "inherit")
style.TextShadow = current.TextShadow;
else
{
// text-shadow: offset-x offset-y | blur | #color
// text-shadow: #color | offset-x offset-y
bool success = true;
std::string prop(it->second);
size_t pos;
pos = prop.find_first_of(",\n\r");
if (pos != std::string::npos)
prop = prop.substr(0, pos);
std::vector<std::string> parts;
NLMISC::splitString(prop, " ", parts);
switch(parts.size())
{
case 1:
{
success = scanHTMLColor(it->second.c_str(), style.TextShadow.Color);
break;
}
// no case 2:
case 3:
{
if (!fromString(parts[0], style.TextShadow.X))
{
success = scanHTMLColor(parts[0].c_str(), style.TextShadow.Color);
success = success && fromString(parts[1], style.TextShadow.X);
success = success && fromString(parts[2], style.TextShadow.Y);
}
else
{
success = fromString(parts[1], style.TextShadow.Y);
success = success && scanHTMLColor(parts[2].c_str(), style.TextShadow.Color);
}
break;
}
case 4:
{
if (!fromString(parts[0], style.TextShadow.X))
{
success = scanHTMLColor(parts[0].c_str(), style.TextShadow.Color);
success = success && fromString(parts[1], style.TextShadow.X);
success = success && fromString(parts[2], style.TextShadow.Y);
// ignore blur [3]
}
else
{
success = fromString(parts[0], style.TextShadow.X);
success = success && fromString(parts[1], style.TextShadow.Y);
// ignore blur [2]
success = success && scanHTMLColor(parts[3].c_str(), style.TextShadow.Color);
}
break;
}
default:
{
// unsupported rule
break;
}
}
style.TextShadow.Enabled = success;
}
}
else
if (it->first == "width")
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.Width = tmpf * Root.FontSize;
else if (unit == "em")
style.Width = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.Width = tmpf;
}
}
else
if (it->first == "height")
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.Height = tmpf * Root.FontSize;
else if (unit == "em")
style.Height = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.Height = tmpf;
}
}
else
if (it->first == "max-width")
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.MaxWidth = tmpf * Root.FontSize;
else if (unit == "em")
style.MaxWidth = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.MaxWidth = tmpf;
}
}
else
if (it->first == "max-height")
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.MaxHeight = tmpf * Root.FontSize;
else if (unit == "em")
style.MaxHeight = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.MaxHeight = tmpf;
}
}
else
if (it->first == "-ryzom-modulate-color")
{
bool b;
if (it->second == "inherit")
style.GlobalColor = current.GlobalColor;
else
if (fromString(it->second, b))
style.GlobalColor = b;
}
else
if (it->first == "background-color")
{
if (it->second == "inherit")
style.BackgroundColor = current.BackgroundColor;
else
scanHTMLColor(it->second.c_str(), style.BackgroundColor);
}
else
if (it->first == "-ryzom-background-color-over")
{
if (it->second == "inherit")
style.BackgroundColorOver = current.BackgroundColorOver;
else
scanHTMLColor(it->second.c_str(), style.BackgroundColorOver);
}
}
// if outer element has underline set, then inner element cannot remove it
if (current.Underlined)
style.Underlined = current.Underlined;
// if outer element has line-through set, then inner element cannot remove it
if (current.StrikeThrough)
style.StrikeThrough = current.StrikeThrough;
}
// ***************************************************************************
void CCssStyle::applyCssMinMax(sint32 &width, sint32 &height, sint32 minw, sint32 minh, sint32 maxw, sint32 maxh) const
{
if (maxw <= 0) maxw = width;
if (maxh <= 0) maxh = height;
maxw = std::max(minw, maxw);
maxh = std::max(minh, maxh);
float ratio = (float) width / std::max(1, height);
if (width > maxw)
{
width = maxw;
height = std::max((sint32)(maxw /ratio), minh);
}
if (width < minw)
{
width = minw;
height = std::min((sint32)(minw / ratio), maxh);
}
if (height > maxh)
{
width = std::max((sint32)(maxh * ratio), minw);
height = maxh;
}
if (height < minh)
{
width = std::min((sint32)(minh * ratio), maxw);
height = minh;
}
if (width > maxw && height > maxh)
{
if (maxw/width <= maxh/height)
{
width = maxw;
height = std::max(minh, (sint32)(maxw / ratio));
}
else
{
width = std::max(minw, (sint32)(maxh * ratio));
height = maxh;
}
}
if (width < minw && height < minh)
{
if (minw / width <= minh / height)
{
width = std::min(maxw, (sint32)(minh * ratio));
height = minh;
}
else
{
width = minw;
height = std::min(maxh, (sint32)(minw / ratio));
}
}
if (width < minw && height > maxh)
{
width = minw;
height = maxh;
}
if (width > maxw && height < minh)
{
width = maxw;
height = minh;
}
}
} // namespace

File diff suppressed because it is too large Load diff

View file

@ -345,6 +345,368 @@ namespace NLGUI
} }
} }
static bool isHexa(char c)
{
return isdigit(c) || (tolower(c) >= 'a' && tolower(c) <= 'f');
}
static uint8 convertHexa(char c)
{
return (uint8) (tolower(c) - (isdigit(c) ? '0' : ('a' - 10)));
}
// scan a color component, and return pointer to next position
static const char *scanColorComponent(const char *src, uint8 &intensity)
{
if (!src) return NULL;
if (!isHexa(*src)) return NULL;
uint8 value = convertHexa(*src++) << 4;
if (!isHexa(*src)) return NULL;
value += convertHexa(*src++);
intensity = value;
return src;
}
static float hueToRgb(float m1, float m2, float h)
{
if (h < 0) h += 1.0f;
if (h > 1) h -= 1.0f;
if (h*6 < 1.0f) return m1 + (m2 - m1)*h*6;
if (h*2 < 1.0f) return m2;
if (h*3 < 2.0f) return m1 + (m2 - m1) * (2.0f/3.0f - h)*6;
return m1;
}
static void hslToRgb(float h, float s, float l, CRGBA &result)
{
float m1, m2;
if (l <= 0.5f)
m2 = l * (s + 1.0f);
else
m2 = l + s - l * s;
m1 = l*2 - m2;
result.R = 255 * hueToRgb(m1, m2, h + 1.0f/3.0f);
result.G = 255 * hueToRgb(m1, m2, h);
result.B = 255 * hueToRgb(m1, m2, h - 1.0f/3.0f);
result.A = 255;
}
class CNameToCol
{
public:
const char *Name;
CRGBA Color;
CNameToCol(const char *name, CRGBA color) : Name(name), Color(color) {}
};
static CNameToCol htmlColorNameToRGBA[] =
{
CNameToCol("AliceBlue", CRGBA(0xF0, 0xF8, 0xFF)),
CNameToCol("AntiqueWhite", CRGBA(0xFA, 0xEB, 0xD7)),
CNameToCol("Aqua", CRGBA(0x00, 0xFF, 0xFF)),
CNameToCol("Aquamarine", CRGBA(0x7F, 0xFF, 0xD4)),
CNameToCol("Azure", CRGBA(0xF0, 0xFF, 0xFF)),
CNameToCol("Beige", CRGBA(0xF5, 0xF5, 0xDC)),
CNameToCol("Bisque", CRGBA(0xFF, 0xE4, 0xC4)),
CNameToCol("Black", CRGBA(0x00, 0x00, 0x00)),
CNameToCol("BlanchedAlmond", CRGBA(0xFF, 0xEB, 0xCD)),
CNameToCol("Blue", CRGBA(0x00, 0x00, 0xFF)),
CNameToCol("BlueViolet", CRGBA(0x8A, 0x2B, 0xE2)),
CNameToCol("Brown", CRGBA(0xA5, 0x2A, 0x2A)),
CNameToCol("BurlyWood", CRGBA(0xDE, 0xB8, 0x87)),
CNameToCol("CadetBlue", CRGBA(0x5F, 0x9E, 0xA0)),
CNameToCol("Chartreuse", CRGBA(0x7F, 0xFF, 0x00)),
CNameToCol("Chocolate", CRGBA(0xD2, 0x69, 0x1E)),
CNameToCol("Coral", CRGBA(0xFF, 0x7F, 0x50)),
CNameToCol("CornflowerBlue", CRGBA(0x64, 0x95, 0xED)),
CNameToCol("Cornsilk", CRGBA(0xFF, 0xF8, 0xDC)),
CNameToCol("Crimson", CRGBA(0xDC, 0x14, 0x3C)),
CNameToCol("Cyan", CRGBA(0x00, 0xFF, 0xFF)),
CNameToCol("DarkBlue", CRGBA(0x00, 0x00, 0x8B)),
CNameToCol("DarkCyan", CRGBA(0x00, 0x8B, 0x8B)),
CNameToCol("DarkGoldenRod", CRGBA(0xB8, 0x86, 0x0B)),
CNameToCol("DarkGray", CRGBA(0xA9, 0xA9, 0xA9)),
CNameToCol("DarkGreen", CRGBA(0x00, 0x64, 0x00)),
CNameToCol("DarkKhaki", CRGBA(0xBD, 0xB7, 0x6B)),
CNameToCol("DarkMagenta", CRGBA(0x8B, 0x00, 0x8B)),
CNameToCol("DarkOliveGreen", CRGBA(0x55, 0x6B, 0x2F)),
CNameToCol("Darkorange", CRGBA(0xFF, 0x8C, 0x00)),
CNameToCol("DarkOrchid", CRGBA(0x99, 0x32, 0xCC)),
CNameToCol("DarkRed", CRGBA(0x8B, 0x00, 0x00)),
CNameToCol("DarkSalmon", CRGBA(0xE9, 0x96, 0x7A)),
CNameToCol("DarkSeaGreen", CRGBA(0x8F, 0xBC, 0x8F)),
CNameToCol("DarkSlateBlue", CRGBA(0x48, 0x3D, 0x8B)),
CNameToCol("DarkSlateGray", CRGBA(0x2F, 0x4F, 0x4F)),
CNameToCol("DarkTurquoise", CRGBA(0x00, 0xCE, 0xD1)),
CNameToCol("DarkViolet", CRGBA(0x94, 0x00, 0xD3)),
CNameToCol("DeepPink", CRGBA(0xFF, 0x14, 0x93)),
CNameToCol("DeepSkyBlue", CRGBA(0x00, 0xBF, 0xFF)),
CNameToCol("DimGray", CRGBA(0x69, 0x69, 0x69)),
CNameToCol("DodgerBlue", CRGBA(0x1E, 0x90, 0xFF)),
CNameToCol("Feldspar", CRGBA(0xD1, 0x92, 0x75)),
CNameToCol("FireBrick", CRGBA(0xB2, 0x22, 0x22)),
CNameToCol("FloralWhite", CRGBA(0xFF, 0xFA, 0xF0)),
CNameToCol("ForestGreen", CRGBA(0x22, 0x8B, 0x22)),
CNameToCol("Fuchsia", CRGBA(0xFF, 0x00, 0xFF)),
CNameToCol("Gainsboro", CRGBA(0xDC, 0xDC, 0xDC)),
CNameToCol("GhostWhite", CRGBA(0xF8, 0xF8, 0xFF)),
CNameToCol("Gold", CRGBA(0xFF, 0xD7, 0x00)),
CNameToCol("GoldenRod", CRGBA(0xDA, 0xA5, 0x20)),
CNameToCol("Gray", CRGBA(0x80, 0x80, 0x80)),
CNameToCol("Green", CRGBA(0x00, 0x80, 0x00)),
CNameToCol("GreenYellow", CRGBA(0xAD, 0xFF, 0x2F)),
CNameToCol("HoneyDew", CRGBA(0xF0, 0xFF, 0xF0)),
CNameToCol("HotPink", CRGBA(0xFF, 0x69, 0xB4)),
CNameToCol("IndianRed ", CRGBA(0xCD, 0x5C, 0x5C)),
CNameToCol("Indigo ", CRGBA(0x4B, 0x00, 0x82)),
CNameToCol("Ivory", CRGBA(0xFF, 0xFF, 0xF0)),
CNameToCol("Khaki", CRGBA(0xF0, 0xE6, 0x8C)),
CNameToCol("Lavender", CRGBA(0xE6, 0xE6, 0xFA)),
CNameToCol("LavenderBlush", CRGBA(0xFF, 0xF0, 0xF5)),
CNameToCol("LawnGreen", CRGBA(0x7C, 0xFC, 0x00)),
CNameToCol("LemonChiffon", CRGBA(0xFF, 0xFA, 0xCD)),
CNameToCol("LightBlue", CRGBA(0xAD, 0xD8, 0xE6)),
CNameToCol("LightCoral", CRGBA(0xF0, 0x80, 0x80)),
CNameToCol("LightCyan", CRGBA(0xE0, 0xFF, 0xFF)),
CNameToCol("LightGoldenRodYellow", CRGBA(0xFA, 0xFA, 0xD2)),
CNameToCol("LightGrey", CRGBA(0xD3, 0xD3, 0xD3)),
CNameToCol("LightGreen", CRGBA(0x90, 0xEE, 0x90)),
CNameToCol("LightPink", CRGBA(0xFF, 0xB6, 0xC1)),
CNameToCol("LightSalmon", CRGBA(0xFF, 0xA0, 0x7A)),
CNameToCol("LightSeaGreen", CRGBA(0x20, 0xB2, 0xAA)),
CNameToCol("LightSkyBlue", CRGBA(0x87, 0xCE, 0xFA)),
CNameToCol("LightSlateBlue", CRGBA(0x84, 0x70, 0xFF)),
CNameToCol("LightSlateGray", CRGBA(0x77, 0x88, 0x99)),
CNameToCol("LightSteelBlue", CRGBA(0xB0, 0xC4, 0xDE)),
CNameToCol("LightYellow", CRGBA(0xFF, 0xFF, 0xE0)),
CNameToCol("Lime", CRGBA(0x00, 0xFF, 0x00)),
CNameToCol("LimeGreen", CRGBA(0x32, 0xCD, 0x32)),
CNameToCol("Linen", CRGBA(0xFA, 0xF0, 0xE6)),
CNameToCol("Magenta", CRGBA(0xFF, 0x00, 0xFF)),
CNameToCol("Maroon", CRGBA(0x80, 0x00, 0x00)),
CNameToCol("MediumAquaMarine", CRGBA(0x66, 0xCD, 0xAA)),
CNameToCol("MediumBlue", CRGBA(0x00, 0x00, 0xCD)),
CNameToCol("MediumOrchid", CRGBA(0xBA, 0x55, 0xD3)),
CNameToCol("MediumPurple", CRGBA(0x93, 0x70, 0xD8)),
CNameToCol("MediumSeaGreen", CRGBA(0x3C, 0xB3, 0x71)),
CNameToCol("MediumSlateBlue", CRGBA(0x7B, 0x68, 0xEE)),
CNameToCol("MediumSpringGreen", CRGBA(0x00, 0xFA, 0x9A)),
CNameToCol("MediumTurquoise", CRGBA(0x48, 0xD1, 0xCC)),
CNameToCol("MediumVioletRed", CRGBA(0xC7, 0x15, 0x85)),
CNameToCol("MidnightBlue", CRGBA(0x19, 0x19, 0x70)),
CNameToCol("MintCream", CRGBA(0xF5, 0xFF, 0xFA)),
CNameToCol("MistyRose", CRGBA(0xFF, 0xE4, 0xE1)),
CNameToCol("Moccasin", CRGBA(0xFF, 0xE4, 0xB5)),
CNameToCol("NavajoWhite", CRGBA(0xFF, 0xDE, 0xAD)),
CNameToCol("Navy", CRGBA(0x00, 0x00, 0x80)),
CNameToCol("OldLace", CRGBA(0xFD, 0xF5, 0xE6)),
CNameToCol("Olive", CRGBA(0x80, 0x80, 0x00)),
CNameToCol("OliveDrab", CRGBA(0x6B, 0x8E, 0x23)),
CNameToCol("Orange", CRGBA(0xFF, 0xA5, 0x00)),
CNameToCol("OrangeRed", CRGBA(0xFF, 0x45, 0x00)),
CNameToCol("Orchid", CRGBA(0xDA, 0x70, 0xD6)),
CNameToCol("PaleGoldenRod", CRGBA(0xEE, 0xE8, 0xAA)),
CNameToCol("PaleGreen", CRGBA(0x98, 0xFB, 0x98)),
CNameToCol("PaleTurquoise", CRGBA(0xAF, 0xEE, 0xEE)),
CNameToCol("PaleVioletRed", CRGBA(0xD8, 0x70, 0x93)),
CNameToCol("PapayaWhip", CRGBA(0xFF, 0xEF, 0xD5)),
CNameToCol("PeachPuff", CRGBA(0xFF, 0xDA, 0xB9)),
CNameToCol("Peru", CRGBA(0xCD, 0x85, 0x3F)),
CNameToCol("Pink", CRGBA(0xFF, 0xC0, 0xCB)),
CNameToCol("Plum", CRGBA(0xDD, 0xA0, 0xDD)),
CNameToCol("PowderBlue", CRGBA(0xB0, 0xE0, 0xE6)),
CNameToCol("Purple", CRGBA(0x80, 0x00, 0x80)),
CNameToCol("Red", CRGBA(0xFF, 0x00, 0x00)),
CNameToCol("RosyBrown", CRGBA(0xBC, 0x8F, 0x8F)),
CNameToCol("RoyalBlue", CRGBA(0x41, 0x69, 0xE1)),
CNameToCol("SaddleBrown", CRGBA(0x8B, 0x45, 0x13)),
CNameToCol("Salmon", CRGBA(0xFA, 0x80, 0x72)),
CNameToCol("SandyBrown", CRGBA(0xF4, 0xA4, 0x60)),
CNameToCol("SeaGreen", CRGBA(0x2E, 0x8B, 0x57)),
CNameToCol("SeaShell", CRGBA(0xFF, 0xF5, 0xEE)),
CNameToCol("Sienna", CRGBA(0xA0, 0x52, 0x2D)),
CNameToCol("Silver", CRGBA(0xC0, 0xC0, 0xC0)),
CNameToCol("SkyBlue", CRGBA(0x87, 0xCE, 0xEB)),
CNameToCol("SlateBlue", CRGBA(0x6A, 0x5A, 0xCD)),
CNameToCol("SlateGray", CRGBA(0x70, 0x80, 0x90)),
CNameToCol("Snow", CRGBA(0xFF, 0xFA, 0xFA)),
CNameToCol("SpringGreen", CRGBA(0x00, 0xFF, 0x7F)),
CNameToCol("SteelBlue", CRGBA(0x46, 0x82, 0xB4)),
CNameToCol("Tan", CRGBA(0xD2, 0xB4, 0x8C)),
CNameToCol("Teal", CRGBA(0x00, 0x80, 0x80)),
CNameToCol("Thistle", CRGBA(0xD8, 0xBF, 0xD8)),
CNameToCol("Tomato", CRGBA(0xFF, 0x63, 0x47)),
CNameToCol("Turquoise", CRGBA(0x40, 0xE0, 0xD0)),
CNameToCol("Violet", CRGBA(0xEE, 0x82, 0xEE)),
CNameToCol("VioletRed", CRGBA(0xD0, 0x20, 0x90)),
CNameToCol("Wheat", CRGBA(0xF5, 0xDE, 0xB3)),
CNameToCol("White", CRGBA(0xFF, 0xFF, 0xFF)),
CNameToCol("WhiteSmoke", CRGBA(0xF5, 0xF5, 0xF5)),
CNameToCol("Yellow", CRGBA(0xFF, 0xFF, 0x00)),
CNameToCol("YellowGreen", CRGBA(0x9A, 0xCD, 0x32))
};
// scan a color from a HTML form (#rrggbb format)
bool scanHTMLColor(const char *src, CRGBA &dest)
{
if (!src || *src == '\0') return false;
if (*src == '#')
{
++src;
if (strlen(src) == 3 || strlen(src) == 4)
{
bool hasAlpha = (strlen(src) == 4);
// check RGB for valid hex
if (isHexa(src[0]) && isHexa(src[1]) && isHexa(src[2]))
{
// check optional A for valid hex
if (hasAlpha && !isHexa(src[3])) return false;
dest.R = convertHexa(src[0]);
dest.G = convertHexa(src[1]);
dest.B = convertHexa(src[2]);
dest.R = dest.R << 4 | dest.R;
dest.G = dest.G << 4 | dest.G;
dest.B = dest.B << 4 | dest.B;
if (hasAlpha)
{
dest.A = convertHexa(src[3]);
dest.A = dest.A << 4 | dest.A;
}
else
dest.A = 255;
return true;
}
return false;
}
CRGBA result;
src = scanColorComponent(src, result.R); if (!src) return false;
src = scanColorComponent(src, result.G); if (!src) return false;
src = scanColorComponent(src, result.B); if (!src) return false;
src = scanColorComponent(src, result.A);
if (!src)
{
// Alpha is optional
result.A = 255;
}
dest = result;
return true;
}
if (strnicmp(src, "rgb(", 4) == 0 || strnicmp(src, "rgba(", 5) == 0)
{
src += 4;
if (*src == '(') src++;
std::vector<std::string> parts;
NLMISC::splitString(src, ",", parts);
if (parts.size() >= 3)
{
CRGBA result;
sint tmpv;
float tmpf;
// R
if (getPercentage(tmpv, tmpf, parts[0].c_str())) tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.R = tmpv;
// G
if (getPercentage(tmpv, tmpf, parts[1].c_str())) tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.G = tmpv;
// B
if (getPercentage(tmpv, tmpf, parts[2].c_str())) tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.B = tmpv;
// A
if (parts.size() == 4)
{
if (!fromString(parts[3], tmpf)) return false;
if (parts[3].find_first_of("%") != std::string::npos)
tmpf /= 100;
tmpv = 255 * tmpf;
clamp(tmpv, 0, 255);
result.A = tmpv;
}
else
result.A = 255;
dest = result;
return true;
}
return false;
}
if (strnicmp(src, "hsl(", 4) == 0 || strnicmp(src, "hsla(", 5) == 0)
{
src += 4;
if (*src == '(') src++;
std::vector<std::string> parts;
NLMISC::splitString(src, ",", parts);
if (parts.size() >= 3)
{
sint tmpv;
float h, s, l;
// hue
if (!fromString(parts[0], tmpv)) return false;
tmpv = ((tmpv % 360) + 360) % 360;
h = (float) tmpv / 360.0f;
// saturation
if (!getPercentage(tmpv, s, parts[1].c_str())) return false;
clamp(s, 0.0f, 1.0f);
// lightness
if (!getPercentage(tmpv, l, parts[2].c_str())) return false;
clamp(l, 0.0f, 1.0f);
CRGBA result;
hslToRgb(h, s, l, result);
// A
if (parts.size() == 4)
{
float tmpf;
if (!fromString(parts[3], tmpf)) return false;
if (parts[3].find_first_of("%") != std::string::npos)
tmpf /= 100;
clamp(tmpf, 0.0f, 1.0f);
result.A = 255 * tmpf;
}
dest = result;
return true;
}
return false;
}
{
// slow but should suffice for now
for(uint k = 0; k < sizeofarray(htmlColorNameToRGBA); ++k)
{
if (nlstricmp(src, htmlColorNameToRGBA[k].Name) == 0)
{
dest = htmlColorNameToRGBA[k].Color;
return true;
}
}
return false;
}
}
// *************************************************************************** // ***************************************************************************
CRGBA getColor (const char *color) CRGBA getColor (const char *color)