Skip to content

Commit fc3cd34

Browse files
committed
Improve color functions
1 parent eeb86aa commit fc3cd34

File tree

5 files changed

+172
-65
lines changed

5 files changed

+172
-65
lines changed

src/context.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,9 +782,13 @@ namespace Sass {
782782
using namespace Functions;
783783
// RGB Functions
784784
register_function(ctx, rgb_sig, rgb, env);
785+
785786
register_overload_stub(ctx, "rgba", env, 4);
786787
register_function(ctx, rgba_4_sig, rgba_4, 4, env);
788+
register_function(ctx, rgba_3_sig, rgba_3, 3, env);
787789
register_function(ctx, rgba_2_sig, rgba_2, 2, env);
790+
register_function(ctx, rgba_1_sig, rgba_1, 1, env);
791+
788792
register_function(ctx, red_sig, red, env);
789793
register_function(ctx, green_sig, green, env);
790794
register_function(ctx, blue_sig, blue, env);

src/fn_colors.cpp

Lines changed: 159 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ namespace Sass {
1313

1414
namespace Functions {
1515

16-
bool string_argument(AST_Node_Obj obj) {
16+
bool isVar(AST_Node_Obj obj) {
17+
String_Constant* s = Cast<String_Constant>(obj);
18+
if (s == nullptr) return false;
19+
const std::string& str = s->value();
20+
return starts_with(str, "var(");
21+
}
22+
23+
bool isSpecialNumber(AST_Node_Obj obj) {
1724
String_Constant* s = Cast<String_Constant>(obj);
1825
if (s == nullptr) return false;
1926
const std::string& str = s->value();
2027
return starts_with(str, "calc(") ||
21-
starts_with(str, "var(");
28+
starts_with(str, "var(");
2229
}
2330

2431
void hsla_alpha_percent_deprecation(const ParserState& pstate, const std::string val)
@@ -35,9 +42,9 @@ namespace Sass {
3542
BUILT_IN(rgb)
3643
{
3744
if (
38-
string_argument(env["$red"]) ||
39-
string_argument(env["$green"]) ||
40-
string_argument(env["$blue"])
45+
isSpecialNumber(env["$red"]) ||
46+
isSpecialNumber(env["$green"]) ||
47+
isSpecialNumber(env["$blue"])
4148
) {
4249
return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("
4350
+ env["$red"]->to_string()
@@ -56,68 +63,156 @@ namespace Sass {
5663
COLOR_NUM("$blue"));
5764
}
5865

66+
67+
/// Asserts that [number] is a percentage or has no units, and normalizes the
68+
/// value.
69+
///
70+
/// If [number] has no units, its value is clamped to be greater than `0` or
71+
/// less than [max] and returned. If [number] is a percentage, it's scaled to be
72+
/// within `0` and [max]. Otherwise, this throws a [SassScriptException].
73+
///
74+
/// [name] is used to identify the argument in the error message.
75+
double _percentageOrUnitless(Number* number, double max, std::string name, Backtraces traces) {
76+
double value = 0.0;
77+
if (!number->hasUnits()) {
78+
value = number->value();
79+
}
80+
else if (number->hasUnit("%")) {
81+
value = max * number->value() / 100;
82+
}
83+
else {
84+
error(name + ": Expected " + number->to_string() +
85+
" to have unit \"%\".", number->pstate(), traces);
86+
}
87+
if (value < 0.0) return 0.0;
88+
if (value > max) return max;
89+
return value;
90+
}
91+
92+
double fuzzyRound(double value) {
93+
return value;
94+
}
95+
96+
AST_Node* getArg(std::string name, Env& env)
97+
{
98+
if (env.has_local(name)) {
99+
return env.get_local(name);
100+
}
101+
return nullptr;
102+
}
103+
104+
Value* _rgb(std::string name, Env& env, Signature sig, ParserState pstate, Backtraces traces)
105+
{
106+
AST_Node* _r = getArg("$red", env);
107+
AST_Node* _g = getArg("$green", env);
108+
AST_Node* _b = getArg("$blue", env);
109+
AST_Node* _a = getArg("$alpha", env);
110+
// Check if any `calc()` or `var()` are passed
111+
if (isSpecialNumber(_r) || isSpecialNumber(_g) || isSpecialNumber(_b) || isSpecialNumber(_a)) {
112+
std::stringstream fncall;
113+
fncall << name << "(";
114+
fncall << _r->to_css() << ", ";
115+
fncall << _g->to_css() << ", ";
116+
fncall << _b->to_css() << ", ";
117+
fncall << _a->to_css() << ")";
118+
return SASS_MEMORY_NEW(StringLiteral, pstate, fncall.str());
119+
}
120+
121+
Number* r = ARGNUM("$red");
122+
Number* g = ARGNUM("$green");
123+
Number* b = ARGNUM("$blue");
124+
Number* a = _a ? ARGNUM("$alpha") : nullptr;
125+
126+
return SASS_MEMORY_NEW(Color_RGBA, pstate,
127+
fuzzyRound(_percentageOrUnitless(r, 255, "red", traces)),
128+
fuzzyRound(_percentageOrUnitless(g, 255, "red", traces)),
129+
fuzzyRound(_percentageOrUnitless(b, 255, "red", traces)),
130+
a ? _percentageOrUnitless(a, 1.0, "alpha", traces) : 1.0);
131+
132+
}
133+
134+
135+
Value* _rgbTwoArg(std::string name, Env& env, Signature sig, ParserState pstate, Backtraces traces)
136+
{
137+
AST_Node* _c = getArg("$color", env);
138+
AST_Node* _a = getArg("$alpha", env);
139+
// Check if any `calc()` or `var()` are passed
140+
if (isVar(_c)) {
141+
std::stringstream fncall;
142+
fncall << name << "("
143+
<< _c->to_css() << ", "
144+
<< _a->to_css() << ")";
145+
return SASS_MEMORY_NEW(StringLiteral,
146+
pstate, fncall.str());
147+
}
148+
else if (isVar(_a)) {
149+
if (Color* c = Cast<Color>(_c)) {
150+
Color_RGBAObj col = c->toRGBA();
151+
std::stringstream fncall;
152+
fncall << name << "("
153+
<< col->r() << ", "
154+
<< col->g() << ", "
155+
<< col->b() << ", "
156+
<< _a->to_css() << ")";
157+
return SASS_MEMORY_NEW(StringLiteral,
158+
pstate, fncall.str());
159+
}
160+
else {
161+
std::stringstream fncall;
162+
fncall << name << "("
163+
<< _c->to_css() << ", "
164+
<< _a->to_css() << ")";
165+
return SASS_MEMORY_NEW(StringLiteral,
166+
pstate, fncall.str());
167+
}
168+
}
169+
else if (isSpecialNumber(_a)) {
170+
Color* c = ARGCOL("$color");
171+
Color_RGBAObj col = c->toRGBA();
172+
std::stringstream fncall;
173+
fncall << name << "("
174+
<< col->r() << ", "
175+
<< col->g() << ", "
176+
<< col->b() << ", "
177+
<< _a->to_css() << ")";
178+
return SASS_MEMORY_NEW(StringLiteral,
179+
pstate, fncall.str());
180+
}
181+
182+
Color* c = ARGCOL("$color");
183+
Number* a = ARGNUM("$alpha");
184+
185+
double alpha = _percentageOrUnitless(a, 1.0, "alpha", traces);
186+
187+
c = SASS_MEMORY_COPY(c);
188+
c->a(alpha);
189+
c->disp("");
190+
return c;
191+
}
192+
59193
Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
60194
BUILT_IN(rgba_4)
61195
{
62-
if (
63-
string_argument(env["$red"]) ||
64-
string_argument(env["$green"]) ||
65-
string_argument(env["$blue"]) ||
66-
string_argument(env["$alpha"])
67-
) {
68-
return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("
69-
+ env["$red"]->to_string()
70-
+ ", "
71-
+ env["$green"]->to_string()
72-
+ ", "
73-
+ env["$blue"]->to_string()
74-
+ ", "
75-
+ env["$alpha"]->to_string()
76-
+ ")"
77-
);
78-
}
196+
return _rgb("rgba", env, rgba_4_sig, pstate, traces);
197+
}
79198

80-
return SASS_MEMORY_NEW(Color_RGBA,
81-
pstate,
82-
COLOR_NUM("$red"),
83-
COLOR_NUM("$green"),
84-
COLOR_NUM("$blue"),
85-
ALPHA_NUM("$alpha"));
199+
Signature rgba_3_sig = "rgba($red, $green, $blue)";
200+
BUILT_IN(rgba_3)
201+
{
202+
return _rgb("rgba", env, rgba_3_sig, pstate, traces);
86203
}
87204

88205
Signature rgba_2_sig = "rgba($color, $alpha)";
89206
BUILT_IN(rgba_2)
90207
{
91-
if (
92-
string_argument(env["$color"])
93-
) {
94-
return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("
95-
+ env["$color"]->to_string()
96-
+ ", "
97-
+ env["$alpha"]->to_string()
98-
+ ")"
99-
);
100-
}
101-
102-
Color_RGBA_Obj c_arg = ARGCOL("$color")->toRGBA();
103-
104-
if (
105-
string_argument(env["$alpha"])
106-
) {
107-
std::stringstream strm;
108-
strm << "rgba("
109-
<< (int)c_arg->r() << ", "
110-
<< (int)c_arg->g() << ", "
111-
<< (int)c_arg->b() << ", "
112-
<< env["$alpha"]->to_string()
113-
<< ")";
114-
return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());
115-
}
208+
return _rgbTwoArg("rgba", env, rgba_2_sig, pstate, traces);
209+
}
116210

117-
Color_RGBA_Obj new_c = SASS_MEMORY_COPY(c_arg);
118-
new_c->a(ALPHA_NUM("$alpha"));
119-
new_c->disp("");
120-
return new_c.detach();
211+
Signature rgba_1_sig = "rgba($channels)";
212+
BUILT_IN(rgba_1)
213+
{
214+
std::cerr << "qweqwe\n";
215+
return nullptr;
121216
}
122217

123218
////////////////
@@ -181,9 +276,9 @@ namespace Sass {
181276
BUILT_IN(hsl)
182277
{
183278
if (
184-
string_argument(env["$hue"]) ||
185-
string_argument(env["$saturation"]) ||
186-
string_argument(env["$lightness"])
279+
isSpecialNumber(env["$hue"]) ||
280+
isSpecialNumber(env["$saturation"]) ||
281+
isSpecialNumber(env["$lightness"])
187282
) {
188283
return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("
189284
+ env["$hue"]->to_string()
@@ -208,10 +303,10 @@ namespace Sass {
208303
BUILT_IN(hsla)
209304
{
210305
if (
211-
string_argument(env["$hue"]) ||
212-
string_argument(env["$saturation"]) ||
213-
string_argument(env["$lightness"]) ||
214-
string_argument(env["$alpha"])
306+
isSpecialNumber(env["$hue"]) ||
307+
isSpecialNumber(env["$saturation"]) ||
308+
isSpecialNumber(env["$lightness"]) ||
309+
isSpecialNumber(env["$alpha"])
215310
) {
216311
return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("
217312
+ env["$hue"]->to_string()

src/fn_colors.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ namespace Sass {
2222

2323
extern Signature rgb_sig;
2424
extern Signature rgba_4_sig;
25+
extern Signature rgba_3_sig;
2526
extern Signature rgba_2_sig;
27+
extern Signature rgba_1_sig;
2628
extern Signature red_sig;
2729
extern Signature green_sig;
2830
extern Signature blue_sig;
@@ -55,7 +57,9 @@ namespace Sass {
5557

5658
BUILT_IN(rgb);
5759
BUILT_IN(rgba_4);
60+
BUILT_IN(rgba_3);
5861
BUILT_IN(rgba_2);
62+
BUILT_IN(rgba_1);
5963
BUILT_IN(red);
6064
BUILT_IN(green);
6165
BUILT_IN(blue);

src/fn_strings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ namespace Sass {
182182
if (end_at > size) { end_at = (double)size; }
183183
if (start_at < 0) {
184184
start_at += size + 1;
185-
if (start_at < 0) start_at = 0;
185+
if (start_at <= 0) start_at = 1;
186186
}
187187
else if (start_at == 0) { ++ start_at; }
188188

src/units.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ namespace Sass {
7373
std::string unit() const;
7474
// get if units are empty
7575
bool is_unitless() const;
76+
77+
bool hasUnits() const {
78+
return !is_unitless();
79+
}
7680
// return if valid for css
7781
bool is_valid_css_unit() const;
7882
// reduce units for output

0 commit comments

Comments
 (0)