initial css color macros. html.d now depends on coloror.d

This commit is contained in:
Adam D. Ruppe 2011-12-13 13:45:07 -05:00
parent f55d4effb7
commit 08e6ee0111
2 changed files with 197 additions and 107 deletions

105
color.d
View File

@ -31,6 +31,14 @@ struct Color {
static Color black() {
return Color(0, 0, 0);
}
string toString() {
import std.string;
if(a == 255)
return format("%02x%02x%02x", r, g, b);
else
return format("%02x%02x%02x%02x", r, g, b, a);
}
}
@ -123,6 +131,42 @@ real[3] toHsl(Color c) {
return [H, S, L];
}
Color lighten(Color c, real percentage) {
auto hsl = toHsl(c);
hsl[2] *= (1 + percentage);
if(hsl[2] > 1)
hsl[2] = 1;
return fromHsl(hsl);
}
Color darken(Color c, real percentage) {
auto hsl = toHsl(c);
hsl[2] *= (1 - percentage);
return fromHsl(hsl);
}
Color rotateHue(Color c, real degrees) {
auto hsl = toHsl(c);
hsl[0] += degrees;
return fromHsl(hsl);
}
Color desaturate(Color c, real percentage) {
auto hsl = toHsl(c);
hsl[1] *= (1 - percentage);
return fromHsl(hsl);
}
Color saturate(Color c, real percentage) {
auto hsl = toHsl(c);
hsl[1] *= (1 + percentage);
if(hsl[1] > 1)
hsl[1] = 1;
return fromHsl(hsl);
}
/*
void main(string[] args) {
auto color1 = toHsl(Color(255, 0, 0));
@ -131,3 +175,64 @@ void main(string[] args) {
writefln("#%02x%02x%02x", color.r, color.g, color.b);
}
*/
/* Color algebra functions */
/* Alpha putpixel looks like this:
void putPixel(Image i, Color c) {
Color b;
b.r = i.data[(y * i.width + x) * bpp + 0];
b.g = i.data[(y * i.width + x) * bpp + 1];
b.b = i.data[(y * i.width + x) * bpp + 2];
b.a = i.data[(y * i.width + x) * bpp + 3];
float ca = cast(float) c.a / 255;
i.data[(y * i.width + x) * bpp + 0] = alpha(c.r, ca, b.r);
i.data[(y * i.width + x) * bpp + 1] = alpha(c.g, ca, b.g);
i.data[(y * i.width + x) * bpp + 2] = alpha(c.b, ca, b.b);
i.data[(y * i.width + x) * bpp + 3] = alpha(c.a, ca, b.a);
}
ubyte alpha(ubyte c1, float alpha, ubyte onto) {
auto got = (1 - alpha) * onto + alpha * c1;
if(got > 255)
return 255;
return cast(ubyte) got;
}
So, given the background color and the resultant color, what was
composited on to it?
*/
ubyte unalpha(ubyte colorYouHave, float alpha, ubyte backgroundColor) {
// resultingColor = (1-alpha) * backgroundColor + alpha * answer
auto resultingColorf = cast(float) colorYouHave;
auto backgroundColorf = cast(float) backgroundColor;
auto answer = (resultingColorf - backgroundColorf + alpha * backgroundColorf) / alpha;
if(answer > 255)
return 255;
if(answer < 0)
return 0;
return cast(ubyte) answer;
}
ubyte makeAlpha(ubyte colorYouHave, ubyte backgroundColor/*, ubyte foreground = 0x00*/) {
//auto foregroundf = cast(float) foreground;
auto foregroundf = 0.00f;
auto colorYouHavef = cast(float) colorYouHave;
auto backgroundColorf = cast(float) backgroundColor;
// colorYouHave = backgroundColorf - alpha * backgroundColorf + alpha * foregroundf
auto alphaf = 1 - colorYouHave / backgroundColorf;
alphaf *= 255;
if(alphaf < 0)
return 0;
if(alphaf > 255)
return 255;
return cast(ubyte) alphaf;
}

199
html.d
View File

@ -9,6 +9,8 @@
module arsd.html;
public import arsd.dom;
import arsd.color;
import std.array;
import std.string;
import std.variant;
@ -197,6 +199,13 @@ string recommendedBasicCssForUserContent = `
`;
string favicon(Document document) {
auto item = document.querySelector("link[rel~=icon]");
if(item !is null)
return item.href;
return "/favicon.ico"; // it pisses me off that the fucking browsers do this.... but they do, so I will too.
}
/// Translates validate="" tags to inline javascript. "this" is the thing
/// being checked.
void translateValidation(Document document) {
@ -1554,6 +1563,11 @@ class CssMacroExpander : MacroExpander {
this() {
super();
functions["prefixed"] = &prefixed;
functions["lighten"] = &(colorFunctionWrapper!lighten);
functions["darken"] = &(colorFunctionWrapper!darken);
functions["rotateHue"] = &(colorFunctionWrapper!rotateHue);
functions["saturate"] = &(colorFunctionWrapper!saturate);
functions["desaturate"] = &(colorFunctionWrapper!desaturate);
}
// prefixed(border-radius: 12px);
@ -1567,6 +1581,23 @@ class CssMacroExpander : MacroExpander {
string expandAndDenest(string cssSrc) {
return cssToString(denestCss(lexCss(this.expand(cssSrc))));
}
dstring colorFunctionWrapper(alias func)(dstring[] args) {
auto color = readCssColor(to!string(args[0]));
auto percentage = readCssNumber(args[1]);
return to!dstring(func(color, percentage).toString());
}
}
real readCssNumber(dstring s) {
s = s.replace(" "d, ""d);
if(s.length == 0)
return 0;
if(s[$-1] == '%')
return (to!real(s[0 .. $-1]) / 100f);
return to!real(s);
}
import std.format;
@ -1627,121 +1658,75 @@ class JavascriptMacroExpander : MacroExpander {
}
string beautifyCss(string css) {
css = css.replace(":", ": ");
css = css.replace(": ", ": ");
css = css.replace("{", " {\n\t");
css = css.replace(";", ";\n\t");
css = css.replace("\t}", "}\n\n");
return css.strip;
}
/+
void main() {
import std.stdio;
int fromHex(string s) {
int result = 0;
writeln((denestCss(`
label {
color: black;
span {
background-color: red;
}
> input {
orange;
}
}
span { hate: vile; }
@import url('adasdsa/asdsa');
cool,
that {
color: white;
this {
border: none;
}
}
`)));
}
+/
/++
This adds nesting, named blocks, and simple macros to css, provided
you follow some rules.
Since it doesn't do a real parsing, you need to put the right
tokens on the right line so it knows what is going on.
1) When nesting, always put the { on the same line.
2) Don't put { on lines without selectors
Examples:
NESTING:
label {
color: red;
span {
}
}
NAMED BLOCKS (for mixing in):
@name(test) {
color: green;
}
input {
@mixin(test);
}
VARIABLES:
@let(a = red); // note these are immutable
div {
color: @var(a); // it's just text replacement...
}
FUNCTIONS:
Functions are pre-defined. The closest you can get
to your own are mixins.
@funname(arg, args...);
OR
@funname(arg, args...) { final_arg }
Unknown function names are passed through without
modification.
It works by extracting mixins first, then expanding nested items,
then mixing in the mixins, and finally, doing variable replacement.
But, you'll see that aside from nesting, it's all done the same
way.
Alas, this doesn't do extra useful things like accessing the
dynamic inherit values because it's just text replacement on
the stylesheet.
@foreach(k; v) {
int exp = 1;
foreach(c; retro(s)) {
if(c >= 'A' && c <= 'F')
result += exp * (c - 'A' + 10);
else if(c >= 'a' && c <= 'f')
result += exp * (c - 'a' + 10);
else if(c >= '0' && c <= '9')
result += exp * (c - '0');
else
throw new Exception("invalid hex character: " ~ cast(char) c);
exp *= 16;
}
for(var counter_1 = 0 < counter_1 < v.length; counter_1++) {
var k = v[counter_1];
/*[original code]*/
}
+/
string improveCss(string css) {
return null;
return result;
}
Color readCssColor(string cssColor) {
cssColor = cssColor.strip().toLower();
if(cssColor.startsWith("#")) {
cssColor = cssColor[1 .. $];
if(cssColor.length == 3) {
cssColor = "" ~ cssColor[0] ~ cssColor[0]
~ cssColor[1] ~ cssColor[1]
~ cssColor[2] ~ cssColor[2];
}
if(cssColor.length == 6)
cssColor ~= "ff";
/* my extension is to do alpha */
if(cssColor.length == 8) {
return Color(
fromHex(cssColor[0 .. 2]),
fromHex(cssColor[2 .. 4]),
fromHex(cssColor[4 .. 6]),
fromHex(cssColor[6 .. 8]));
} else
throw new Exception("invalid color " ~ cssColor);
} else if(cssColor.startsWith("rgba")) {
assert(0); // FIXME: implement
/*
cssColor = cssColor.replace("rgba", "");
cssColor = cssColor.replace(" ", "");
cssColor = cssColor.replace("(", "");
cssColor = cssColor.replace(")", "");
auto parts = cssColor.split(",");
*/
} else if(cssColor.startsWith("rgb")) {
assert(0); // FIXME: implement
} else if(cssColor.startsWith("hsl")) {
assert(0); // FIXME: implement
} else
switch(cssColor) {
default:
// FIXME let's go ahead and try naked hex for compatibility with my gradient program
assert(0, "Unknown color: " ~ cssColor);
}
}