W3C home > Mailing lists > Public > www-style@w3.org > April 2010

RE: [css3-background] box-shadow spread radius and rounded corners

From: Brian Manthos <brianman@microsoft.com>
Date: Wed, 28 Apr 2010 12:39:32 +0000
To: Brad Kemper <brad.kemper@gmail.com>, Tab Atkins Jr. <jackalmage@gmail.com>
CC: Sylvain Galineau <sylvaing@microsoft.com>, fantasai <fantasai.lists@inkedblade.net>, "www-style@w3.org" <www-style@w3.org>, "Prabs Chawla" <pchawla@microsoft.com>
Message-ID: <FA122FEC823D524CB516E4E0374D9DCF014E8141@TK5EX14MBXC136.redmond.corp.microsoft.com>
> I don't know why you'd find that suprising. When you have a thick  
> border, the radius on the inside of the border-radius is different  
> from the radius on the outside. Think of this as a sort of extra  
> border-width around the shadow shape, but with border-radius defining  
> the inside radius of that extra borders's corners instead of the  
> outside radius.

----- Regarding "extra border-width around the shadow shape"

I don't like this way of thinking about it for a number of reasons.

1. It's no longer a shadow of the original shape.  It's a shadow of some related shape not the original shape.  It's like you have a margin-edge-curve and are shadowing that.  It's dramatically diverged from what I feel is the common sense concept of a shadow.


2. Applying the "it's another curvature knob to twiddle" approach completely goes against the letter of the old and new specifications.  Specifically:
# The shadow should not change shape when a spread
# radius is applied: sharp corners should remain sharp.

If Brad's description (or related derivatives) become the decided approach, then that quote needs to be stricken and replaced with something else completely.

Further this quote also contradicts Brad's description:
# If the box has a nonzero 'border-radius',
# the shadow is rounded in the same way

As Tab noted, Brad's description makes rounded corners vanish if you specify a spread radius at least as large as the border-radius.

This quote should also be stricken/rewritten if Brad's description is the preferred way of thinking of box-shadow.


3. It's more complicated and more expensive.

GBB=GeometryBorderBox
GPB=GeometryPaddingBox

You can no longer think of shadow as one of:
a. non-inset as GBB->Translate(H,V)->Scale(spreadFactor)->Exclude(GBB)
b. inset as GPB->Exclude(GPB->Translate(H,V)->Scale(spreadFactor))

Note that here spreadFactor is calculated as a function of element height, element width, and the spreadRadius.


Instead it becomes something more like...
a. non-inset as BuildNewGeometry(GBB->Offset(H,V),spreadRadius)->Exclude(GBB)
b. inset as GPB->Exclude(BuildNewGeometry(GBB->Offset(H,V),spreadRadius))

The BuildNewGeometry injected in the middle of the pipeline messes up everything because it's not simple transform.

Not only is this more confusing and more complicated, it foils some hardware acceleration opportunities because of the lack of simple transforms. Instead UAs are forced to generate entirely different geometries before it ever reaches hardware.


4. It requires reworking existing implementations.  Firefox and Opera's current renderings do not match Brad's description, but are rather more in line with my expectation that it's intended to be a pure stretch of the original border or padding edge shape.



-----Regarding aspect ratio

Assuming we step away from Brad's proposal for a moment and focus on the spec 
phrasing again:

# The shadow should not change shape when a spread radius is applied

# If the box has a nonzero 'border-radius',
# the shadow is rounded in the same way

# Positive values cause the shadow to grow
# in all directions by the specified radius

Combining these together, my interpretation is that the aspect ratio of the border-box (non-inset) or padding-box shape (inset) should be retained in the produced shadow shape prior to excluding the source geometry from the shape (non-inset) or vice-versa (inset).

An example makes this clearer (hopefully)...

div {
    background-clip:padding-box;
    background-color:blue;
    border-radius:50px;
    border-width:25px 0px;
    box-shadow:inset 30px 40px 0px 25px rgba(255,255,255,0.5), 10px 20px 0px 50px rgba(0,0,0,0.5);
    width:400px;
    height:100px;
}

BBR=Border-box rectangle
BBG=Border-box geometry
PBR=Padding-box rectangle
PBG=Padding-box geometry
SOGU=Shadow outer geometry, unclipped
SOGC=Shadow outer geometry (clipped)
SIGU=Shadow inner geometry, unclipped
SIGC=Shadow inner geometry (clipped)

I would expect rendering to involve something like the following steps:
1. Start with rectangle 400px wide by 100px tall, call it BBR
2. Round the corners of BBR to produce BBG which will be the equivalent of producing the union of two semicircles with 50px radii with a rectangle 300px wide by 100px tall
3. Create new rectangle PBR that is 350px wide by 100px tall by doing:
   a. PBR=BBR
   b. PBR.left += 25px
   c. PBR.right -= 25px
4. The effective radii for PBG are fortunately consistent across corners:
   a. Rx = 50px + 25px = 75px
   b. Ry = 50px + 0px = 50px
5. Round the corners of PBR to produce PBG using the radii from step 4
6. Build SOGU as follows:
   a. TempGeometry2 = BBG->Translate(10px, 20px)
   b. maxdimensionBB = max(BBR.Width(), BBR.Height()) = 400px
   c. spreadFactor2 = (spreadRadius2 + maxdimensionBB + spreadRadius2) / maxdimensionBB = (50px + 400px + 50px) / 400px = 5/4 = 1.25
   d. centerPoint2 = { BBR.Center().x + 10px, BBR.Center().y + 20px }
   e. SOGU = TempGeometry2->Scale(spreadFactor2, centerPoint2)
7. Build SOGC as follows:
   a. SOGC = SOGU->Exclude(BBG)
8. Fill SOGC with black using 50% opacity
9. Fill PBG with blue
10. Build SIGU as follow:
   a. TempGeometry2 = PBG->Translate(30px, 40px)
   b. maxdimensionPB = max(PBR.Width(), PBR.Height()) = 350px
   c. spreadFactor1 = (spreadRadius1 + maxdimensionPB + spreadRadius2) / maxdimensionPB = (25px + 350px + 25px) / 350px = 400/350 = 8/7ths
   d. centerpoint1 = { PBR.Center().x + 30px, PBR.Center().y + 40px) }
   e. SIGU = TempGeometry1->Scale(spreadFactor1, centerPoint1)
11. Build SIGC as follows:
   a. SIGC = PBG->Exclude(SIGU)
12. Fill SIGC with white using 50% opacity

Note that I intentionally used 0px blur radius to avoid the distraction.

It's early morning here so I'm not at full steam, but I think I got most of it captured.


Regarding that algorithm and current UAs ...

I believe Firefox's rendering (and I think Opera's, but I mostly looked at the 'fox) roughly matches my sequence with one adjustment.

In steps 6c, 6e, 10c, and 10e the spread factor is applied in the X and Y dimensions differently.  More specifically, the calculation in 6b and 10b does not appear to impact the rendering.

I believe that approach is incorrect as it violates the letter of the three spec quotes above when they are considered in combination.

-Brian
Received on Wednesday, 28 April 2010 12:40:25 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:26 GMT