1 // sass.hpp must go before all system headers to get the
2 // __EXTENSIONS__ fix on Solaris.
3 #include "sass.hpp"
4 
5 #include <iomanip>
6 #include "ast.hpp"
7 #include "fn_utils.hpp"
8 #include "fn_colors.hpp"
9 #include "util.hpp"
10 #include "util_string.hpp"
11 
12 namespace Sass {
13 
14   namespace Functions {
15 
string_argument(AST_Node_Obj obj)16     bool string_argument(AST_Node_Obj obj) {
17       String_Constant* s = Cast<String_Constant>(obj);
18       if (s == nullptr) return false;
19       const sass::string& str = s->value();
20       return starts_with(str, "calc(") ||
21              starts_with(str, "var(");
22     }
23 
hsla_alpha_percent_deprecation(const SourceSpan & pstate,const sass::string val)24     void hsla_alpha_percent_deprecation(const SourceSpan& pstate, const sass::string val)
25     {
26 
27       sass::string msg("Passing a percentage as the alpha value to hsla() will be interpreted");
28       sass::string tail("differently in future versions of Sass. For now, use " + val + " instead.");
29 
30       deprecated(msg, tail, false, pstate);
31 
32     }
33 
34     Signature rgb_sig = "rgb($red, $green, $blue)";
BUILT_IN(rgb)35     BUILT_IN(rgb)
36     {
37       if (
38         string_argument(env["$red"]) ||
39         string_argument(env["$green"]) ||
40         string_argument(env["$blue"])
41       ) {
42         return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("
43                                                         + env["$red"]->to_string()
44                                                         + ", "
45                                                         + env["$green"]->to_string()
46                                                         + ", "
47                                                         + env["$blue"]->to_string()
48                                                         + ")"
49         );
50       }
51 
52       return SASS_MEMORY_NEW(Color_RGBA,
53                              pstate,
54                              COLOR_NUM("$red"),
55                              COLOR_NUM("$green"),
56                              COLOR_NUM("$blue"));
57     }
58 
59     Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
BUILT_IN(rgba_4)60     BUILT_IN(rgba_4)
61     {
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       }
79 
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"));
86     }
87 
88     Signature rgba_2_sig = "rgba($color, $alpha)";
BUILT_IN(rgba_2)89     BUILT_IN(rgba_2)
90     {
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 = ARG("$color", Color)->toRGBA();
103 
104       if (
105         string_argument(env["$alpha"])
106       ) {
107         sass::ostream 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       }
116 
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();
121     }
122 
123     ////////////////
124     // RGB FUNCTIONS
125     ////////////////
126 
127     Signature red_sig = "red($color)";
BUILT_IN(red)128     BUILT_IN(red)
129     {
130       Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();
131       return SASS_MEMORY_NEW(Number, pstate, color->r());
132     }
133 
134     Signature green_sig = "green($color)";
BUILT_IN(green)135     BUILT_IN(green)
136     {
137       Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();
138       return SASS_MEMORY_NEW(Number, pstate, color->g());
139     }
140 
141     Signature blue_sig = "blue($color)";
BUILT_IN(blue)142     BUILT_IN(blue)
143     {
144       Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();
145       return SASS_MEMORY_NEW(Number, pstate, color->b());
146     }
147 
colormix(Context & ctx,SourceSpan & pstate,Color * color1,Color * color2,double weight)148     Color_RGBA* colormix(Context& ctx, SourceSpan& pstate, Color* color1, Color* color2, double weight) {
149       Color_RGBA_Obj c1 = color1->toRGBA();
150       Color_RGBA_Obj c2 = color2->toRGBA();
151       double p = weight/100;
152       double w = 2*p - 1;
153       double a = c1->a() - c2->a();
154 
155       double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;
156       double w2 = 1 - w1;
157 
158       return SASS_MEMORY_NEW(Color_RGBA,
159                              pstate,
160                              Sass::round(w1*c1->r() + w2*c2->r(), ctx.c_options.precision),
161                              Sass::round(w1*c1->g() + w2*c2->g(), ctx.c_options.precision),
162                              Sass::round(w1*c1->b() + w2*c2->b(), ctx.c_options.precision),
163                              c1->a()*p + c2->a()*(1-p));
164     }
165 
166     Signature mix_sig = "mix($color1, $color2, $weight: 50%)";
BUILT_IN(mix)167     BUILT_IN(mix)
168     {
169       Color_Obj  color1 = ARG("$color1", Color);
170       Color_Obj  color2 = ARG("$color2", Color);
171       double weight = DARG_U_PRCT("$weight");
172       return colormix(ctx, pstate, color1, color2, weight);
173 
174     }
175 
176     ////////////////
177     // HSL FUNCTIONS
178     ////////////////
179 
180     Signature hsl_sig = "hsl($hue, $saturation, $lightness)";
BUILT_IN(hsl)181     BUILT_IN(hsl)
182     {
183       if (
184         string_argument(env["$hue"]) ||
185         string_argument(env["$saturation"]) ||
186         string_argument(env["$lightness"])
187       ) {
188         return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("
189                                                         + env["$hue"]->to_string()
190                                                         + ", "
191                                                         + env["$saturation"]->to_string()
192                                                         + ", "
193                                                         + env["$lightness"]->to_string()
194                                                         + ")"
195         );
196       }
197 
198       return SASS_MEMORY_NEW(Color_HSLA,
199         pstate,
200         ARGVAL("$hue"),
201         ARGVAL("$saturation"),
202         ARGVAL("$lightness"),
203         1.0);
204 
205     }
206 
207     Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";
BUILT_IN(hsla)208     BUILT_IN(hsla)
209     {
210       if (
211         string_argument(env["$hue"]) ||
212         string_argument(env["$saturation"]) ||
213         string_argument(env["$lightness"]) ||
214         string_argument(env["$alpha"])
215       ) {
216         return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("
217                                                         + env["$hue"]->to_string()
218                                                         + ", "
219                                                         + env["$saturation"]->to_string()
220                                                         + ", "
221                                                         + env["$lightness"]->to_string()
222                                                         + ", "
223                                                         + env["$alpha"]->to_string()
224                                                         + ")"
225         );
226       }
227 
228       Number* alpha = ARG("$alpha", Number);
229       if (alpha && alpha->unit() == "%") {
230         Number_Obj val = SASS_MEMORY_COPY(alpha);
231         val->numerators.clear(); // convert
232         val->value(val->value() / 100.0);
233         sass::string nr(val->to_string(ctx.c_options));
234         hsla_alpha_percent_deprecation(pstate, nr);
235       }
236 
237       return SASS_MEMORY_NEW(Color_HSLA,
238         pstate,
239         ARGVAL("$hue"),
240         ARGVAL("$saturation"),
241         ARGVAL("$lightness"),
242         ARGVAL("$alpha"));
243 
244     }
245 
246     /////////////////////////////////////////////////////////////////////////
247     // Query functions
248     /////////////////////////////////////////////////////////////////////////
249 
250     Signature hue_sig = "hue($color)";
BUILT_IN(hue)251     BUILT_IN(hue)
252     {
253       Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();
254       return SASS_MEMORY_NEW(Number, pstate, col->h(), "deg");
255     }
256 
257     Signature saturation_sig = "saturation($color)";
BUILT_IN(saturation)258     BUILT_IN(saturation)
259     {
260       Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();
261       return SASS_MEMORY_NEW(Number, pstate, col->s(), "%");
262     }
263 
264     Signature lightness_sig = "lightness($color)";
BUILT_IN(lightness)265     BUILT_IN(lightness)
266     {
267       Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();
268       return SASS_MEMORY_NEW(Number, pstate, col->l(), "%");
269     }
270 
271     /////////////////////////////////////////////////////////////////////////
272     // HSL manipulation functions
273     /////////////////////////////////////////////////////////////////////////
274 
275     Signature adjust_hue_sig = "adjust-hue($color, $degrees)";
BUILT_IN(adjust_hue)276     BUILT_IN(adjust_hue)
277     {
278       Color* col = ARG("$color", Color);
279       double degrees = ARGVAL("$degrees");
280       Color_HSLA_Obj copy = col->copyAsHSLA();
281       copy->h(absmod(copy->h() + degrees, 360.0));
282       return copy.detach();
283     }
284 
285     Signature lighten_sig = "lighten($color, $amount)";
BUILT_IN(lighten)286     BUILT_IN(lighten)
287     {
288       Color* col = ARG("$color", Color);
289       double amount = DARG_U_PRCT("$amount");
290       Color_HSLA_Obj copy = col->copyAsHSLA();
291       copy->l(clip(copy->l() + amount, 0.0, 100.0));
292       return copy.detach();
293 
294     }
295 
296     Signature darken_sig = "darken($color, $amount)";
BUILT_IN(darken)297     BUILT_IN(darken)
298     {
299       Color* col = ARG("$color", Color);
300       double amount = DARG_U_PRCT("$amount");
301       Color_HSLA_Obj copy = col->copyAsHSLA();
302       copy->l(clip(copy->l() - amount, 0.0, 100.0));
303       return copy.detach();
304     }
305 
306     Signature saturate_sig = "saturate($color, $amount: false)";
BUILT_IN(saturate)307     BUILT_IN(saturate)
308     {
309       // CSS3 filter function overload: pass literal through directly
310       if (!Cast<Number>(env["$amount"])) {
311         return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");
312       }
313 
314       Color* col = ARG("$color", Color);
315       double amount = DARG_U_PRCT("$amount");
316       Color_HSLA_Obj copy = col->copyAsHSLA();
317       copy->s(clip(copy->s() + amount, 0.0, 100.0));
318       return copy.detach();
319     }
320 
321     Signature desaturate_sig = "desaturate($color, $amount)";
BUILT_IN(desaturate)322     BUILT_IN(desaturate)
323     {
324       Color* col = ARG("$color", Color);
325       double amount = DARG_U_PRCT("$amount");
326       Color_HSLA_Obj copy = col->copyAsHSLA();
327       copy->s(clip(copy->s() - amount, 0.0, 100.0));
328       return copy.detach();
329     }
330 
331     Signature grayscale_sig = "grayscale($color)";
BUILT_IN(grayscale)332     BUILT_IN(grayscale)
333     {
334       // CSS3 filter function overload: pass literal through directly
335       Number* amount = Cast<Number>(env["$color"]);
336       if (amount) {
337         return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");
338       }
339 
340       Color* col = ARG("$color", Color);
341       Color_HSLA_Obj copy = col->copyAsHSLA();
342       copy->s(0.0); // just reset saturation
343       return copy.detach();
344     }
345 
346     /////////////////////////////////////////////////////////////////////////
347     // Misc manipulation functions
348     /////////////////////////////////////////////////////////////////////////
349 
350     Signature complement_sig = "complement($color)";
BUILT_IN(complement)351     BUILT_IN(complement)
352     {
353       Color* col = ARG("$color", Color);
354       Color_HSLA_Obj copy = col->copyAsHSLA();
355       copy->h(absmod(copy->h() - 180.0, 360.0));
356       return copy.detach();
357     }
358 
359     Signature invert_sig = "invert($color, $weight: 100%)";
BUILT_IN(invert)360     BUILT_IN(invert)
361     {
362       // CSS3 filter function overload: pass literal through directly
363       Number* amount = Cast<Number>(env["$color"]);
364       double weight = DARG_U_PRCT("$weight");
365       if (amount) {
366         // TODO: does not throw on 100% manually passed as value
367         if (weight < 100.0) {
368           error("Only one argument may be passed to the plain-CSS invert() function.", pstate, traces);
369         }
370         return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");
371       }
372 
373       Color* col = ARG("$color", Color);
374       Color_RGBA_Obj inv = col->copyAsRGBA();
375       inv->r(clip(255.0 - inv->r(), 0.0, 255.0));
376       inv->g(clip(255.0 - inv->g(), 0.0, 255.0));
377       inv->b(clip(255.0 - inv->b(), 0.0, 255.0));
378       return colormix(ctx, pstate, inv, col, weight);
379     }
380 
381     /////////////////////////////////////////////////////////////////////////
382     // Opacity functions
383     /////////////////////////////////////////////////////////////////////////
384 
385     Signature alpha_sig = "alpha($color)";
386     Signature opacity_sig = "opacity($color)";
BUILT_IN(alpha)387     BUILT_IN(alpha)
388     {
389       String_Constant* ie_kwd = Cast<String_Constant>(env["$color"]);
390       if (ie_kwd) {
391         return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");
392       }
393 
394       // CSS3 filter function overload: pass literal through directly
395       Number* amount = Cast<Number>(env["$color"]);
396       if (amount) {
397         return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");
398       }
399 
400       return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a());
401     }
402 
403     Signature opacify_sig = "opacify($color, $amount)";
404     Signature fade_in_sig = "fade-in($color, $amount)";
BUILT_IN(opacify)405     BUILT_IN(opacify)
406     {
407       Color* col = ARG("$color", Color);
408       double amount = DARG_U_FACT("$amount");
409       Color_Obj copy = SASS_MEMORY_COPY(col);
410       copy->a(clip(col->a() + amount, 0.0, 1.0));
411       return copy.detach();
412     }
413 
414     Signature transparentize_sig = "transparentize($color, $amount)";
415     Signature fade_out_sig = "fade-out($color, $amount)";
BUILT_IN(transparentize)416     BUILT_IN(transparentize)
417     {
418       Color* col = ARG("$color", Color);
419       double amount = DARG_U_FACT("$amount");
420       Color_Obj copy = SASS_MEMORY_COPY(col);
421       copy->a(std::max(col->a() - amount, 0.0));
422       return copy.detach();
423     }
424 
425     ////////////////////////
426     // OTHER COLOR FUNCTIONS
427     ////////////////////////
428 
429     Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
BUILT_IN(adjust_color)430     BUILT_IN(adjust_color)
431     {
432       Color* col = ARG("$color", Color);
433       Number* r = Cast<Number>(env["$red"]);
434       Number* g = Cast<Number>(env["$green"]);
435       Number* b = Cast<Number>(env["$blue"]);
436       Number* h = Cast<Number>(env["$hue"]);
437       Number* s = Cast<Number>(env["$saturation"]);
438       Number* l = Cast<Number>(env["$lightness"]);
439       Number* a = Cast<Number>(env["$alpha"]);
440 
441       bool rgb = r || g || b;
442       bool hsl = h || s || l;
443 
444       if (rgb && hsl) {
445         error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);
446       }
447       else if (rgb) {
448         Color_RGBA_Obj c = col->copyAsRGBA();
449         if (r) c->r(c->r() + DARG_R_BYTE("$red"));
450         if (g) c->g(c->g() + DARG_R_BYTE("$green"));
451         if (b) c->b(c->b() + DARG_R_BYTE("$blue"));
452         if (a) c->a(c->a() + DARG_R_FACT("$alpha"));
453         return c.detach();
454       }
455       else if (hsl) {
456         Color_HSLA_Obj c = col->copyAsHSLA();
457         if (h) c->h(c->h() + absmod(h->value(), 360.0));
458         if (s) c->s(c->s() + DARG_R_PRCT("$saturation"));
459         if (l) c->l(c->l() + DARG_R_PRCT("$lightness"));
460         if (a) c->a(c->a() + DARG_R_FACT("$alpha"));
461         return c.detach();
462       }
463       else if (a) {
464         Color_Obj c = SASS_MEMORY_COPY(col);
465         c->a(c->a() + DARG_R_FACT("$alpha"));
466         c->a(clip(c->a(), 0.0, 1.0));
467         return c.detach();
468       }
469       error("not enough arguments for `adjust-color'", pstate, traces);
470       // unreachable
471       return col;
472     }
473 
474     Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
BUILT_IN(scale_color)475     BUILT_IN(scale_color)
476     {
477       Color* col = ARG("$color", Color);
478       Number* r = Cast<Number>(env["$red"]);
479       Number* g = Cast<Number>(env["$green"]);
480       Number* b = Cast<Number>(env["$blue"]);
481       Number* h = Cast<Number>(env["$hue"]);
482       Number* s = Cast<Number>(env["$saturation"]);
483       Number* l = Cast<Number>(env["$lightness"]);
484       Number* a = Cast<Number>(env["$alpha"]);
485 
486       bool rgb = r || g || b;
487       bool hsl = h || s || l;
488 
489       if (rgb && hsl) {
490         error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);
491       }
492       else if (rgb) {
493         Color_RGBA_Obj c = col->copyAsRGBA();
494         double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;
495         double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;
496         double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;
497         double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;
498         if (rscale) c->r(c->r() + rscale * (rscale > 0.0 ? 255.0 - c->r() : c->r()));
499         if (gscale) c->g(c->g() + gscale * (gscale > 0.0 ? 255.0 - c->g() : c->g()));
500         if (bscale) c->b(c->b() + bscale * (bscale > 0.0 ? 255.0 - c->b() : c->b()));
501         if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));
502         return c.detach();
503       }
504       else if (hsl) {
505         Color_HSLA_Obj c = col->copyAsHSLA();
506         double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;
507         double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;
508         double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;
509         double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;
510         if (hscale) c->h(c->h() + hscale * (hscale > 0.0 ? 360.0 - c->h() : c->h()));
511         if (sscale) c->s(c->s() + sscale * (sscale > 0.0 ? 100.0 - c->s() : c->s()));
512         if (lscale) c->l(c->l() + lscale * (lscale > 0.0 ? 100.0 - c->l() : c->l()));
513         if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));
514         return c.detach();
515       }
516       else if (a) {
517         Color_Obj c = SASS_MEMORY_COPY(col);
518         double ascale = DARG_R_PRCT("$alpha") / 100.0;
519         c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));
520         c->a(clip(c->a(), 0.0, 1.0));
521         return c.detach();
522       }
523       error("not enough arguments for `scale-color'", pstate, traces);
524       // unreachable
525       return col;
526     }
527 
528     Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
BUILT_IN(change_color)529     BUILT_IN(change_color)
530     {
531       Color* col = ARG("$color", Color);
532       Number* r = Cast<Number>(env["$red"]);
533       Number* g = Cast<Number>(env["$green"]);
534       Number* b = Cast<Number>(env["$blue"]);
535       Number* h = Cast<Number>(env["$hue"]);
536       Number* s = Cast<Number>(env["$saturation"]);
537       Number* l = Cast<Number>(env["$lightness"]);
538       Number* a = Cast<Number>(env["$alpha"]);
539 
540       bool rgb = r || g || b;
541       bool hsl = h || s || l;
542 
543       if (rgb && hsl) {
544         error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);
545       }
546       else if (rgb) {
547         Color_RGBA_Obj c = col->copyAsRGBA();
548         if (r) c->r(DARG_U_BYTE("$red"));
549         if (g) c->g(DARG_U_BYTE("$green"));
550         if (b) c->b(DARG_U_BYTE("$blue"));
551         if (a) c->a(DARG_U_FACT("$alpha"));
552         return c.detach();
553       }
554       else if (hsl) {
555         Color_HSLA_Obj c = col->copyAsHSLA();
556         if (h) c->h(absmod(h->value(), 360.0));
557         if (s) c->s(DARG_U_PRCT("$saturation"));
558         if (l) c->l(DARG_U_PRCT("$lightness"));
559         if (a) c->a(DARG_U_FACT("$alpha"));
560         return c.detach();
561       }
562       else if (a) {
563         Color_Obj c = SASS_MEMORY_COPY(col);
564         c->a(clip(DARG_U_FACT("$alpha"), 0.0, 1.0));
565         return c.detach();
566       }
567       error("not enough arguments for `change-color'", pstate, traces);
568       // unreachable
569       return col;
570     }
571 
572     Signature ie_hex_str_sig = "ie-hex-str($color)";
BUILT_IN(ie_hex_str)573     BUILT_IN(ie_hex_str)
574     {
575       Color* col = ARG("$color", Color);
576       Color_RGBA_Obj c = col->toRGBA();
577       double r = clip(c->r(), 0.0, 255.0);
578       double g = clip(c->g(), 0.0, 255.0);
579       double b = clip(c->b(), 0.0, 255.0);
580       double a = clip(c->a(), 0.0, 1.0) * 255.0;
581 
582       sass::ostream ss;
583       ss << '#' << std::setw(2) << std::setfill('0');
584       ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));
585       ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));
586       ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));
587       ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision));
588 
589       sass::string result = ss.str();
590       Util::ascii_str_toupper(&result);
591       return SASS_MEMORY_NEW(String_Quoted, pstate, result);
592     }
593 
594   }
595 
596 }
597