Skip to content

Commit

Permalink
Implement internal HSLA color representation
Browse files Browse the repository at this point in the history
  • Loading branch information
mgreter committed Dec 13, 2018
1 parent aabb867 commit aa9852b
Show file tree
Hide file tree
Showing 28 changed files with 860 additions and 760 deletions.
19 changes: 19 additions & 0 deletions src/ast.cpp
Expand Up @@ -511,6 +511,25 @@ namespace Sass {
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

Expression::Expression(ParserState pstate, bool d, bool e, bool i, Type ct)
: AST_Node(pstate),
is_delayed_(d),
is_expanded_(e),
is_interpolant_(i),
concrete_type_(ct)
{ }

Expression::Expression(const Expression* ptr)
: AST_Node(ptr),
is_delayed_(ptr->is_delayed_),
is_expanded_(ptr->is_expanded_),
is_interpolant_(ptr->is_interpolant_),
concrete_type_(ptr->concrete_type_)
{ }

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

Unary_Expression::Unary_Expression(ParserState pstate, Type t, Expression_Obj o)
: Expression(pstate), optype_(t), operand_(o), hash_(0)
{ }
Expand Down
19 changes: 2 additions & 17 deletions src/ast.hpp
Expand Up @@ -80,7 +80,7 @@ namespace Sass {
case LT: return "lt";
case LTE: return "lte";
case ADD: return "plus";
case SUB: return "sub";
case SUB: return "minus";
case MUL: return "times";
case DIV: return "div";
case MOD: return "mod";
Expand Down Expand Up @@ -198,21 +198,7 @@ namespace Sass {
ADD_PROPERTY(bool, is_interpolant)
ADD_PROPERTY(Type, concrete_type)
public:
Expression(ParserState pstate,
bool d = false, bool e = false, bool i = false, Type ct = NONE)
: AST_Node(pstate),
is_delayed_(d),
is_expanded_(e),
is_interpolant_(i),
concrete_type_(ct)
{ }
Expression(const Expression* ptr)
: AST_Node(ptr),
is_delayed_(ptr->is_delayed_),
is_expanded_(ptr->is_expanded_),
is_interpolant_(ptr->is_interpolant_),
concrete_type_(ptr->concrete_type_)
{ }
Expression(ParserState pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);
virtual operator bool() { return true; }
virtual ~Expression() { }
virtual bool is_invisible() const { return false; }
Expand Down Expand Up @@ -441,7 +427,6 @@ namespace Sass {
ADD_PROPERTY(bool, group_end)
public:
Statement(ParserState pstate, Type st = NONE, size_t t = 0);
Statement(const Statement* ptr); // copy constructor
virtual ~Statement() = 0; // virtual destructor
// needed for rearranging nested rulesets during CSS emission
virtual bool bubbles();
Expand Down
5 changes: 4 additions & 1 deletion src/ast2c.cpp
Expand Up @@ -16,9 +16,12 @@ namespace Sass {
union Sass_Value* AST2C::operator()(Custom_Error_Ptr e)
{ return sass_make_error(e->message().c_str()); }

union Sass_Value* AST2C::operator()(Color_Ptr c)
union Sass_Value* AST2C::operator()(Color_RGBA_Ptr c)
{ return sass_make_color(c->r(), c->g(), c->b(), c->a()); }

union Sass_Value* AST2C::operator()(Color_HSLA_Ptr c)
{ return operator()(c->toRGBA()); }

union Sass_Value* AST2C::operator()(String_Constant_Ptr s)
{
if (s->quote_mark()) {
Expand Down
3 changes: 2 additions & 1 deletion src/ast2c.hpp
Expand Up @@ -16,7 +16,8 @@ namespace Sass {

union Sass_Value* operator()(Boolean_Ptr);
union Sass_Value* operator()(Number_Ptr);
union Sass_Value* operator()(Color_Ptr);
union Sass_Value* operator()(Color_RGBA_Ptr);
union Sass_Value* operator()(Color_HSLA_Ptr);
union Sass_Value* operator()(String_Constant_Ptr);
union Sass_Value* operator()(String_Quoted_Ptr);
union Sass_Value* operator()(Custom_Warning_Ptr);
Expand Down
2 changes: 2 additions & 0 deletions src/ast_def_macros.hpp
Expand Up @@ -74,6 +74,7 @@ public: \
virtual klass##_Ptr clone(std::string, size_t) const = 0; \

#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \
klass(const klass* ptr); \
virtual klass##_Ptr copy(std::string, size_t) const override = 0; \
virtual klass##_Ptr clone(std::string, size_t) const override = 0; \

Expand All @@ -89,6 +90,7 @@ public: \
virtual klass##_Ptr clone() const = 0; \

#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \
klass(const klass* ptr); \
virtual klass##_Ptr copy() const override = 0; \
virtual klass##_Ptr clone() const override = 0; \

Expand Down
1 change: 1 addition & 0 deletions src/ast_fwd_decl.cpp
Expand Up @@ -19,6 +19,7 @@ namespace Sass {
IMPLEMENT_BASE_CAST(Has_Block)
IMPLEMENT_BASE_CAST(PreValue)
IMPLEMENT_BASE_CAST(Value)
IMPLEMENT_BASE_CAST(Color)
IMPLEMENT_BASE_CAST(List)
IMPLEMENT_BASE_CAST(String)
IMPLEMENT_BASE_CAST(String_Constant)
Expand Down
9 changes: 9 additions & 0 deletions src/ast_fwd_decl.hpp
Expand Up @@ -166,6 +166,12 @@ namespace Sass {
class Color;
typedef Color* Color_Ptr;
typedef Color const* Color_Ptr_Const;
class Color_RGBA;
typedef Color_RGBA* Color_RGBA_Ptr;
typedef Color_RGBA const* Color_RGBA_Ptr_Const;
class Color_HSLA;
typedef Color_HSLA* Color_HSLA_Ptr;
typedef Color_HSLA const* Color_HSLA_Ptr_Const;
class Boolean;
typedef Boolean* Boolean_Ptr;
typedef Boolean const* Boolean_Ptr_Const;
Expand Down Expand Up @@ -321,6 +327,8 @@ namespace Sass {
IMPL_MEM_OBJ(Variable);
IMPL_MEM_OBJ(Number);
IMPL_MEM_OBJ(Color);
IMPL_MEM_OBJ(Color_RGBA);
IMPL_MEM_OBJ(Color_HSLA);
IMPL_MEM_OBJ(Boolean);
IMPL_MEM_OBJ(String_Schema);
IMPL_MEM_OBJ(String);
Expand Down Expand Up @@ -477,6 +485,7 @@ namespace Sass {
DECLARE_BASE_CAST(PreValue)
DECLARE_BASE_CAST(Value)
DECLARE_BASE_CAST(List)
DECLARE_BASE_CAST(Color)
DECLARE_BASE_CAST(String)
DECLARE_BASE_CAST(String_Constant)
DECLARE_BASE_CAST(Supports_Condition)
Expand Down
2 changes: 0 additions & 2 deletions src/ast_selectors.hpp
Expand Up @@ -50,7 +50,6 @@ namespace Sass {
mutable size_t hash_;
public:
Selector(ParserState pstate);
Selector(const Selector* ptr);
virtual ~Selector() = 0;
size_t hash() const override = 0;
virtual unsigned long specificity() const = 0;
Expand Down Expand Up @@ -115,7 +114,6 @@ namespace Sass {
HASH_PROPERTY(bool, has_ns)
public:
Simple_Selector(ParserState pstate, std::string n = "");
Simple_Selector(const Simple_Selector* ptr);
virtual std::string ns_name() const;
size_t hash() const override;
bool empty() const;
Expand Down
178 changes: 165 additions & 13 deletions src/ast_values.cpp
Expand Up @@ -210,7 +210,7 @@ namespace Sass {
{
return sass_op_to_name(optype());
}

const std::string Binary_Expression::separator()
{
return sass_op_separator(optype());
Expand All @@ -221,7 +221,7 @@ namespace Sass {
return is_left_interpolant() ||
is_right_interpolant();
}

void Binary_Expression::set_delayed(bool delayed)
{
right()->set_delayed(delayed);
Expand Down Expand Up @@ -485,24 +485,52 @@ namespace Sass {
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

Color::Color(ParserState pstate, double r, double g, double b, double a, const std::string disp)
: Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp),
Color::Color(ParserState pstate, double a, const std::string disp)
: Value(pstate),
disp_(disp), a_(a),
hash_(0)
{ concrete_type(COLOR); }

Color::Color(const Color* ptr)
: Value(ptr),
r_(ptr->r_),
g_(ptr->g_),
b_(ptr->b_),
: Value(ptr->pstate()),
// reset on copy
disp_(""),
a_(ptr->a_),
disp_(ptr->disp_),
hash_(ptr->hash_)
{ concrete_type(COLOR); }

bool Color::operator== (const Expression& rhs) const
{
if (auto r = Cast<Color>(&rhs)) {
if (auto r = Cast<Color_RGBA>(&rhs)) {
return *this == *r;
}
else if (auto r = Cast<Color_HSLA>(&rhs)) {
return *this == *r;
}
else if (auto r = Cast<Color>(&rhs)) {
return a_ == r->a();
}
return false;
}

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

Color_RGBA::Color_RGBA(ParserState pstate, double r, double g, double b, double a, const std::string disp)
: Color(pstate, a, disp),
r_(r), g_(g), b_(b)
{ concrete_type(COLOR); }

Color_RGBA::Color_RGBA(const Color_RGBA* ptr)
: Color(ptr),
r_(ptr->r_),
g_(ptr->g_),
b_(ptr->b_)
{ concrete_type(COLOR); }

bool Color_RGBA::operator== (const Expression& rhs) const
{
if (auto r = Cast<Color_RGBA>(&rhs)) {
return r_ == r->r() &&
g_ == r->g() &&
b_ == r->b() &&
Expand All @@ -511,17 +539,140 @@ namespace Sass {
return false;
}

size_t Color::hash() const
size_t Color_RGBA::hash() const
{
if (hash_ == 0) {
hash_ = std::hash<double>()(a_);
hash_ = std::hash<std::string>()("RGBA");
hash_combine(hash_, std::hash<double>()(a_));
hash_combine(hash_, std::hash<double>()(r_));
hash_combine(hash_, std::hash<double>()(g_));
hash_combine(hash_, std::hash<double>()(b_));
}
return hash_;
}

Color_HSLA_Ptr Color_RGBA::toHSLA(bool copy)
{

// Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
double r = r_ / 255.0;
double g = g_ / 255.0;
double b = b_ / 255.0;

double max = std::max(r, std::max(g, b));
double min = std::min(r, std::min(g, b));
double delta = max - min;

double h = 0;
double s;
double l = (max + min) / 2.0;

if (NEAR_EQUAL(max, min)) {
h = s = 0; // achromatic
}
else {
if (l < 0.5) s = delta / (max + min);
else s = delta / (2.0 - max - min);

if (r == max) h = (g - b) / delta + (g < b ? 6 : 0);
else if (g == max) h = (b - r) / delta + 2;
else if (b == max) h = (r - g) / delta + 4;
}

// HSL hsl_struct;
h = h * 60;
s = s * 100;
l = l * 100;

return SASS_MEMORY_NEW(Color_HSLA,
pstate(), h, s, l, a(), ""
);
}

Color_RGBA_Ptr Color_RGBA::toRGBA(bool copy)
{
return copy ? SASS_MEMORY_COPY(this) : this;
}

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

Color_HSLA::Color_HSLA(ParserState pstate, double h, double s, double l, double a, const std::string disp)
: Color(pstate, a, disp),
h_(absmod(h, 360.0)),
s_(clip(s, 0.0, 100.0)),
l_(clip(l, 0.0, 100.0))
// hash_(0)
{ concrete_type(COLOR); }

Color_HSLA::Color_HSLA(const Color_HSLA* ptr)
: Color(ptr),
h_(ptr->h_),
s_(ptr->s_),
l_(ptr->l_)
// hash_(ptr->hash_)
{ concrete_type(COLOR); }

bool Color_HSLA::operator== (const Expression& rhs) const
{
if (auto r = Cast<Color_HSLA>(&rhs)) {
return h_ == r->h() &&
s_ == r->s() &&
l_ == r->l() &&
a_ == r->a();
}
return false;
}

size_t Color_HSLA::hash() const
{
if (hash_ == 0) {
hash_ = std::hash<std::string>()("HSLA");
hash_combine(hash_, std::hash<double>()(a_));
hash_combine(hash_, std::hash<double>()(h_));
hash_combine(hash_, std::hash<double>()(s_));
hash_combine(hash_, std::hash<double>()(l_));
}
return hash_;
}

// hue to RGB helper function
double h_to_rgb(double m1, double m2, double h)
{
h = absmod(h, 1.0);
if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;
if (h*2.0 < 1) return m2;
if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;
return m1;
}

Color_RGBA_Ptr Color_HSLA::toRGBA(bool copy)
{

double h = absmod(h_ / 360.0, 1.0);
double s = clip(s_ / 100.0, 0.0, 1.0);
double l = clip(l_ / 100.0, 0.0, 1.0);

// Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.
double m2;
if (l <= 0.5) m2 = l*(s+1.0);
else m2 = (l+s)-(l*s);
double m1 = (l*2.0)-m2;
// round the results -- consider moving this into the Color constructor
double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0);
double g = (h_to_rgb(m1, m2, h) * 255.0);
double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0);

return SASS_MEMORY_NEW(Color_RGBA,
pstate(), r, g, b, a(), ""
);
}

Color_HSLA_Ptr Color_HSLA::toHSLA(bool copy)
{
return copy ? SASS_MEMORY_COPY(this) : this;
}

/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -796,7 +947,8 @@ namespace Sass {
IMPLEMENT_AST_OPERATORS(Function_Call);
IMPLEMENT_AST_OPERATORS(Variable);
IMPLEMENT_AST_OPERATORS(Number);
IMPLEMENT_AST_OPERATORS(Color);
IMPLEMENT_AST_OPERATORS(Color_RGBA);
IMPLEMENT_AST_OPERATORS(Color_HSLA);
IMPLEMENT_AST_OPERATORS(Custom_Error);
IMPLEMENT_AST_OPERATORS(Custom_Warning);
IMPLEMENT_AST_OPERATORS(Boolean);
Expand Down

0 comments on commit aa9852b

Please sign in to comment.