diff --git a/color.d b/color.d index 3cb1a71..19e2f41 100644 --- a/color.d +++ b/color.d @@ -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; +} diff --git a/html.d b/html.d index d578817..193341c 100644 --- a/html.d +++ b/html.d @@ -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); + } }