Archive for September, 2010

Learning SVG: lesson 3

I looked at edge bevel in last lesson. It’s time to move on, so in this time I’ll study: more edge bevel. Yup.

When googling for beveling techniques, I found that people often call the following method “bevel”: take a shape, give it some gradient; make a larger version of the same shape, give a inverted gradient, and put it behind the original shape. Sort of like:

Now I’m all for taking shortcuts when possible, but not when it’s incorrect. Granted, I didn’t put much effort to make my example look as good as it could have been, but this method is inherently wrong: it only sort of works if the shape in question is more or less regular–what if you want to bevel a pretzel shaped thing? In this method, the pixel colors on the “bevel” doesn’t depend on the nearby edges, but rather the position in the overall geometry. It’s a fake bevel–more so than other methods as far as manipulating pixels is concerned.

There is another often cited method, however, which is valid: the lighting method. There are defined <feSpecularLight> and <feDiffusedLight> filters, and more often than not, people refer to the use of the former “bevel”. I think that’s also not quite right: you can call it “embossing”, which isn’t the same as “edge bevel”. The diffused light method does work, though, up to some degree (I’m probably too dense to figure it out yet, it really should work completely).

First, this is what a diffused light filter could look like:

		<filter id="difflight" x="-50%" y="-50%" width="200%" height="200%">
			<feGaussianBlur stdDeviation="1" result="blur"/>
			<feDiffuseLighting surfaceScale="3" diffuseConstant="1">
				<feDistantLight azimuth="45" elevation="45"
						lighting-color="white"/>
			</feDiffuseLighting>
		</filter>

Diff lighting is simply doing bump mapping on the alpha channel, meaning the resulting image’s brightness only differs from background where the opacity is not constant; it becomes brighter or darker depending on whether the direction of the opacity gradient is along the light or not. Without the blur in the begining, the edges would only be 1 pixel thick. Anyway, the filter on our star-circle-square trio produces:

In theory, all we need to do is take the edge part, superimpose it on top of the original image, and we are done. Unfortunately, this is what I haven’t figured out how to do properly. I could make two blurs, one larger than the other (by that I mean dilating it), and subtract the two, the result would be the mask containing only the edge part–but that seems redudant, because if I’m going to make two Gaussian blurs and take the difference, I don’t need the lighting at all:

		<filter id="gauss-diff" x="-50%" y="-50%" width="200%" height="200%">
			<feGaussianBlur stdDeviation="1" result="blur"/>

			<feOffset in="blur" dx="1" dy="1" result="blur1"/>
			<feOffset in="blur" dx="-1" dy="-1" result="blur2"/>

			<feComposite in="blur2" in2="blur1" operator="arithmetic" k2="1" k3="-1" result="diff1"/>
			<feFlood flood-color="black"/>
			<feComposite in2="diff1" operator="in" result="diff1"/>

			<feComposite in="blur1" in2="blur2" operator="arithmetic" k2="1" k3="-1" result="diff2"/>
			<feFlood flood-color="white"/>
			<feComposite in2="diff2" operator="in" result="diff2"/>

			<feMerge>
				<feMergeNode in="SourceGraphic"/>
				<feMergeNode in="diff1"/>
				<feMergeNode in="diff2"/>
			</feMerge>

which looks like this:

But still, the edges of diffused lighting look so nice, there has to be a way to use it. Need to think harder.

Anyway, I’m probably done with edge bevels–for now. Here’s a combined bevel/shadow filter to wrap things up:

		<filter id="bevel-shadow" x="-50%" y="-50%" width="200%" height="200%">
			<feGaussianBlur stdDeviation="1" result="blur"/>

			<feOffset in="blur" dx="1" dy="1" result="blur1"/>
			<feOffset in="blur" dx="0" dy="0" result="blur2"/>

			<feComposite in="blur2" in2="blur1" operator="arithmetic" k2="1" k3="-1" result="diff1"/>
			<feFlood flood-color="black"/>
			<feComposite in2="diff1" operator="in" result="diff1"/>

			<feComposite in="blur1" in2="blur2" operator="arithmetic" k2="1" k3="-1" result="diff2"/>
			<feFlood flood-color="white" flood-opacity="0.5"/>
			<feComposite in2="diff2" operator="in" result="diff2"/>

			<feMorphology operator="erode" radius="2" in="SourceAlpha"/>
			<feGaussianBlur stdDeviation="2" result="blur"/>
			<feOffset dy="3" dx="3"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="-1" k3="1" result="shadowDiff"/>
			<feFlood flood-color="black" flood-opacity=".8"/>
			<feComposite in2="shadowDiff" operator="in" result="shadow"/>

			<feMerge>
				<feMergeNode in="SourceGraphic"/>
				<feMergeNode in="shadow"/>
				<feMergeNode in="diff1"/>
				<feMergeNode in="diff2"/>
			</feMerge>
		</filter>

and the result:

Thus ends lesson 3.

Leave a comment

Learning SVG: lesson 2

Innershadow

After I successfully mastered the art of dropshadow, what next?

Looking at Photoshop layer styles, the next thing on the list is innershadow. Yawn. More Gaussian blur and… wait, it’s actually more complicated than I first thought.

Blurring a shape results in a new shape that’s more opaque in the center and fades away towards the edge. Innershadow is the exact opposite. The solution, it turns out, is to subtract the blurred opacity from the original shape. A picture is better than a thousand words:


The idea is, take the original image (first row), blur the alpha mask (white in the second row), subtract the blurred alpha (black in the same row), take the difference (third row), composite it on top of the original image (last row). Code:

<svg	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink"
	width="250" height="90">
	<def>
		<linearGradient id="greenBg" x1="0" y1="0" x2="0" y2="100%">
			<stop offset="0" stop-color="#7d9a94"/>
			<stop offset="1" stop-color="#aab9b4"/>
		</linearGradient>
		<pattern id="checker" patternUnits="userSpaceOnUse"
				x="0" y="0" width="20" height="20">
			<rect x="0" y="0" height="20" width="20" fill="#aaaaaa"/>
			<rect x="0" y="0" height="10" width="10" fill="#999999"/>
			<rect x="10" y="10" height="10" width="10" fill="#999999"/>
		</pattern>
		<g id="obj">
		<path d="M 40.342584,6.7575912 49.849205,26.134167 71.215132,29.187812
			55.724621,44.216837 59.422867,65.480669 40.342583,55.392542
			21.262297,65.480668 24.960545,44.216837 9.470034,29.18781
			30.83596,26.134167 40.342584,6.7575912 z"/>
		<circle cx="110" cy="40" r="30"/>
		<rect x="160" y="15" width="50" height="50"/>
		</g>
		<filter id="innershadow" x0="-50%" y0="-50%" width="200%" height="200%">
			<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
			<feOffset dy="3" dx="3"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="-1" k3="1" result="shadowDiff"/>
			<feFlood flood-color="black" flood-opacity="1"/>
			<feComposite in2="shadowDiff" operator="in"/>
			<feComposite in2="SourceGraphic" operator="over"/>
		</filter>
	</def>

	<rect x0="0" y0="0" width="250" height="90" fill="url(#checker)"/>
	<use xlink:href="#obj" style="fill:url(#greenBg);stroke:none;filter:url(#innershadow)"/>
</svg>

The main thing is shown in bold. Visually, this kind of sharp-edged inner shadow describes holes on the plane, with the edges casting shadows beneath them - why am I explaining this?

The <feFlood> is there if I wanted the shadow to be some other color/opacity. Curiously, I thought putting flood-color="black" flood-opacity="1" there would have no effect here because it’s basically replacing black with black-but with it the shadow is visibly darker. I must have misunderstood the <feComposite> above it (see, I’m new at this).

Naturally, if I can drop a black shadow from top left, I can drop a white shadow (pardon the oxymoron) from bottom right; just change the offset and the flood-fill color, i.e.

			<feOffset dy="-2" dx="-2" />
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="-1" k3="1" result="shadowDiff"/>
			<feFlood flood-color="white" flood-opacity="1"/>

like this:

Visually, this kind of sharp-edged inner white shadow describes … ok it doesn’t quite look like anything, yet. Just wait.

Bevel

Although the white shadow by itself doesn’t quite look like anything, when combined with the black one, we got the bevel effect:

		<filter id="innerbevel" x0="-50%" y0="-50%" width="200%" height="200%">
			<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
			<feOffset dy="-1" dx="-1"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="-1" k3="1" result="hlDiff"/>
			<feFlood flood-color="white" flood-opacity=".7"/>
			<feComposite in2="hlDiff" operator="in"/>
			<feComposite in2="SourceGraphic" operator="over" result="withGlow"/>

			<feOffset in="blur" dy="3" dx="3"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="-1" k3="1" result="shadowDiff"/>
			<feFlood flood-color="black" flood-opacity="1"/>
			<feComposite in2="shadowDiff" operator="in"/>
			<feComposite in2="withGlow" operator="over"/>
		</filter>

like this:

Now there are two ways to look at it: either it’s an embossing and light comes from bottom right, or it’s an engraving and light is from the other direction. Your pick. I’m not sure if this is because my inadequate skill, or rather it’s something intrinsic, but I do remember having the same problem looking at the photos of Moon craters: sometimes they suddenly appear to be bulging up. Is there a word for this phenomenon?

The above is similar to Photoshop inner bevel filter. With some modification, the outerbevel can be done, too:

		<filter id="outerbevel" x0="-50%" y0="-50%" width="200%" height="200%">
			<feMorphology in="SourceAlpha" operator="dilate" radius="1"/>
			<feGaussianBlur stdDeviation="1" result="blur"/>

			<feOffset dy="1" dx="1"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="1" k3="-1" result="hlDiff"/>
			<feFlood flood-color="white" flood-opacity=".7"/>
			<feComposite in2="hlDiff" operator="in"/>
			<feComposite in2="SourceGraphic" operator="over" result="withGlow"/>

			<feOffset in="blur" dy="-1" dx="-1"/>
			<feComposite in2="SourceAlpha" operator="arithmetic"
					k2="1" k3="-1" result="shadowDiff"/>
			<feFlood flood-color="black" flood-opacity="1"/>
			<feComposite in2="shadowDiff" operator="in"/>
			<feComposite in2="withGlow" operator="over"/>
		</filter>

Alas, all renders are not created equal. Gimp (the software, duh) thinks it looks like this:

white Firefox thinks it looks like

I tend to side with Firefox on this one. Either way, though, the effect is less ambiguous than the inner bevel.

In principle, one should be able to make two Gaussians with different sigmas, and use the difference of those to generate the edge effects, thus handling both the inner and outer bevel at the same time.
Just a thought, haven’t tried it yet.

If we take both inner and outer bevel and stack them together (also flip the light direction for the inner one),
we get a crude version of pillow emboss:

Again, this is taken from screen shot of Firefox. Gimp renders the outer part way too dark.

I think I’ver learned enough today to call this a sastifactory lesson 2. Go me.

Leave a comment

Learning SVG

This is not something like a book that helps you learn SVG; rather, it’s more like a notebook I keep to myself while I’m learning SVG-and I’m green, so there, that’s cleared up, I hope.

Basic Shapes

First things first, I need some shapes to work on, so I made these (admittedly boring looking) shapes:
star, circle and square

Code for that:

<svg	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink"
		width="240" height="90">
	<def>
		<pattern id="checker" patternUnits="userSpaceOnUse"
				x="0" y="0" width="20" height="20">
			<rect x="0" y="0" height="20" width="20" fill="#aaaaaa"/>
			<rect x="0" y="0" height="10" width="10" fill="#999999"/>
			<rect x="10" y="10" height="10" width="10" fill="#999999"/>
		</pattern>
		<g id="obj">
			<path d="M 40.342584,6.7575912 49.849205,26.134167
				71.215132,29.187812 55.724621,44.216837 59.422867,65.480669
				40.342583,55.392542 21.262297,65.480668 24.960545,44.216837
				9.470034,29.18781 30.83596,26.134167 40.342584,6.7575912 z"/>
			<circle cx="110" cy="40" r="30"/>
			<rect x="160" y="15" width="50" height="50"/>
		</g>
	</def>
	<rect x0="0" y0="0" width="100%" height="100%" fill="url(#checker)"/>
	<use xlink:href="#obj" style="fill:green;stroke:none"/>
</svg>

Yay! I have my first hand-made svg file! (Ok, the path data for the star I lifted from an Inkscape output, but still…)

So I learned how to use <path>, <rect> and <circle> tags; while the “pattern” thing is just the checkered background. There are more types of shapes one can define in SVG, but I’ll stick with these for now.

Gradient

Well, flat green is boring. I know, I’ll put a gradient on them there shapes:

Code:

<svg	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink"
		width="240" height="90">
	<def>
		<linearGradient id="greenBg" x1="0" y1="0" x2="0" y2="100%">
			<stop offset="0" stop-color="#7d9a94"/>
			<stop offset="1" stop-color="#aab9b4"/>
		</linearGradient>
		<pattern id="checker" patternUnits="userSpaceOnUse"
				x="0" y="0" width="20" height="20">
			<rect x="0" y="0" height="20" width="20" fill="#aaaaaa"/>
			<rect x="0" y="0" height="10" width="10" fill="#999999"/>
			<rect x="10" y="10" height="10" width="10" fill="#999999"/>
		</pattern>
		<g id="obj">
			<path d="M 40.342584,6.7575912 49.849205,26.134167
				71.215132,29.187812 55.724621,44.216837 59.422867,65.480669
				40.342583,55.392542 21.262297,65.480668 24.960545,44.216837
				9.470034,29.18781 30.83596,26.134167 40.342584,6.7575912 z"/>
			<circle cx="110" cy="40" r="30"/>
			<rect x="160" y="15" width="50" height="50"/>
		</g>
	</def>
	<rect x0="0" y0="0" width="100%" height="100%" fill="url(#checker)"/>
	<use xlink:href="#obj" style="fill:url(#greenBg);stroke:none"/>
</svg>

Now instead of the flat, boring green, I have a … flat, boring gradient. Well. Wait, there’s some kind of filter stuff I can do, that oughta spice things up. Hmm.

Filter

It turns out SVG filters is no easy matter. Fun and powerful, though. Let me try some!

What’s the most used (read: abused) graphical effect? Hands down: dropshadow. So dropshadow it is.

I already knew how a dropshadow is made: you take an image, get the alpha channel from it, blur it somewhat, and put it behind the original image. Luckily, there is a <feGaussianBlur> filter tag in SVG, so let’s make a filter from that:

<svg	xmlns="http://www.w3.org/2000/svg"
		xmlns:xlink="http://www.w3.org/1999/xlink"
		width="240" height="100">
	<def>
		<linearGradient id="greenBg" x1="0" y1="0" x2="0" y2="100%">
			<stop offset="0" stop-color="#aab9b4"/>
			<stop offset="1" stop-color="#7d9a94"/>
		</linearGradient>
		<pattern id="checker" patternUnits="userSpaceOnUse"
				x="0" y="0" width="20" height="20">
			<rect x="0" y="0" height="20" width="20" fill="#aaaaaa"/>
			<rect x="0" y="0" height="10" width="10" fill="#999999"/>
			<rect x="10" y="10" height="10" width="10" fill="#999999"/>
		</pattern>
		<g id="obj">
		<path d="M 40.342584,6.7575912 49.849205,26.134167 71.215132,29.187812
			55.724621,44.216837 59.422867,65.480669 40.342583,55.392542
			21.262297,65.480668 24.960545,44.216837 9.470034,29.18781
			30.83596,26.134167 40.342584,6.7575912 z"/>
		<circle cx="113" cy="40" r="30"/>
		<rect x="160" y="15" width="50" height="50"/>
		</g>
		<filter id="dropshadow" x0="-50%" y0="-50%" width="200%" height="200%">
			<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
		</filter>
	</def>

	<rect x0="0" y0="0" width="600" height="600" fill="url(#checker)"/>
	<use xlink:href="#obj" style="fill:url(#greenBg);stroke:none;filter:url(#dropshadow)"/>
</svg>

Easy breezy. The result is, er:

So the filter above does produce the dropshaow, and only the dropshadow. I still need to put the original shapes back in there somehow. I could just draw it again, without any filter this time, so the second copy of the shapes with its exciting gradient sits on top of the shadow. But that means every time I apply the filter on an object, I need to redraw that object again, which is somehow not very satisfying (I’m peculiar, I know). After digging some more on the W3C SVG website, ah! There’s the <feComposite> thingy. Does “Porter-Duff” ring a bell? Anyway.

So the above code is changed into

...
		<filter id="dropshadow" x0="-50%" y0="-50%" width="200%" height="200%">
			<feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
			<feOffset dx="3" dy="4" result="blur" />
			<feComposite in="SourceGraphic" in2="blur" operator="over"/>
		</filter>
...

and the result is more like what I had in mind:

The shadow is also moved slightly to the lower-right, to give the illusion of light coming from top left (the <feOffset> did that).

So far, so good. But anybody can make a black dropshadow, and I want to stand out. I want a red one. I’m not sure what is the generally agreed method of doing that, but the way I manged to do it is flood fill with a color I want (<feFlood flood-color="a color I want"/>), then composite it in the shadow. Basically, it fills the whole area with said color, then copies the alpha values of the shadow to this colored block, so we end up with my choice color, but in the shape of the shadow. Code:

		<filter id="dropshadow" x0="-50%" y0="-50%" width="200%" height="200%">
			<feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
			<feOffset dx="3" dy="4" result="blur" />
			<feFlood flood-color="maroon" />
			<feComposite in2="blur" operator="in" result="colorShadow"/>
			<feComposite in="SourceGraphic" in2="colorShadow" operator="over"/>
		</filter>

the result (when I said “red”, I meant “maroon”. I’m not insane):

Now I saw that it is good. Just the red shadow I wanted. Except it looks a little skimpy. I could make it wider by making the stdDeviation of the Gaussian blur bigger, so the blurring extends further. Currently it’s 2, let’s try, say, 8:

			<feGaussianBlur in="SourceAlpha" stdDeviation="8"/>

Now it becomes:

It is wider, but now it’s skimpy in another way: the shadow is not solid enough. In Photoshop, when doing dropshadows, besides the sigma (the stdDeviation here), there’s also another parameter called “spread”, which dictates how far the shadow extends outwards before it starts fading. In the same vein, I need to make the shapes “fatter” before bluring it, so that the shadows will be more solid for a longer distance. The “enfattening” (is that a word?) is done by <feEnfatten>, I mean, <feMorphology> filter, with the “dilate” operator:

		<filter id="dropshadow" x0="-50%" y0="-50%" width="200%" height="200%">
			<feMorphology in="SourceAlpha" radius="6" operator="dilate"/>
			<feGaussianBlur stdDeviation="2"/>
			<feOffset dx="3" dy="4" result="blur" />
			<feFlood flood-color="maroon" />
			<feComposite in2="blur" operator="in" result="colorShadow"/>
			<feComposite in="SourceGraphic" in2="colorShadow" operator="over"/>
		</filter>

The blurring radius is back to 2 as before, but first we extend the shapes by 6 first. Now look at my spunky, gory red shadow:

It is obviously bad for health, but in name of science, it was worth it. Now I’ll just wrap up my first lesson with a more reasonable looking specimen:

		<filter id="dropshadow" x0="-50%" y0="-50%" width="200%" height="200%">
			<feMorphology in="SourceAlpha" radius="1" operator="dilate"/>
			<feGaussianBlur stdDeviation="3"/>
			<feOffset dx="4" dy="5" result="blur" />
			<feFlood flood-color="#442222" flood-opacity="0.7"/>
			<feComposite in2="blur" operator="in" result="colorShadow"/>
			<feComposite in="SourceGraphic" in2="colorShadow" operator="over"/>
		</filter>

Now I know how to do dropshadows, I’ll call it lesson 1. Well done, me.

Leave a comment