Permalink
Browse files

Fix issues with rendering of rounded borders:

 - Box path calculation has been improved to ensure a correct (squared) path when the shrink is greater than the radius.
 - Rewrote border renderer to use only fills with "eofill" subpaths to make the cut-outs for dashed/dotted/double styles.
 - Dashes are now centered along the edge like WebKit does.
 - Removed all logic around VML stroke as it is no longer used.
 - Flattened the getBoxPath method signature to avoid unnecessary transient object creation.
Fixes issue #11
  • Loading branch information...
1 parent 1c3fb0d commit 47dc9d864157dc0ad9b4dd9b60a03a81e875600b Jason Johnston committed Nov 13, 2011
@@ -50,8 +50,7 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
shape.setSize( bounds.w, bounds.h );
shape.setAttrs(
- 'stroked', false,
- 'path', this.getBoxPath( 0, 2 )
+ 'path', this.getBoxPath( 0, 0, 0, 0, 2 )
);
shape.setFillAttrs( 'color', color.colorValue( el ) );
alpha = color.alpha();
@@ -84,8 +83,7 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
shape = this.getShape( 'bgImage' + i, this.shapeZIndex + ( .5 - i / 1000 ) );
shape.setAttrs(
- 'stroked', false,
- 'path', this.getBoxPath( 0, 2 )
+ 'path', this.getBoxPath( 0, 0, 0, 0, 2 )
);
shape.setSize( w, h );
@@ -100,7 +100,6 @@ PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
var shape = this.getShape( 'borderImage' + name, this.shapeZIndex );
shape.tagName = 'rect';
shape.setAttrs(
- 'stroked', false,
'filled', false
);
return shape;
View

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -50,7 +50,7 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
// round the corners of the expanded shadow shape rather than squaring them off.
radii = PIE.BorderRadiusStyleInfo.ALL_ZERO;
}
- path = me.getBoxPath( { t: shrink, r: shrink, b: shrink, l: shrink }, 2, radii );
+ path = me.getBoxPath( shrink, shrink, shrink, shrink, 2, radii );
// Create the shape object
shape = me.getShape( 'shadow' + i, me.shapeZIndex + ( .5 - i / 1000 ) );
@@ -99,8 +99,6 @@ PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
}
shape.setAttrs(
- 'stroked', false,
- 'filled', true,
'path', path
);
shape.setFillAttrs( 'color', color );
View
@@ -46,13 +46,13 @@ PIE.ImgRenderer = PIE.RendererBase.newRenderer( {
}
shape.setAttrs(
- 'stroked', false,
- 'path', this.getBoxPath( {
- t: round( borderWidths['t'].pixels( el ) + getLength( cs.paddingTop ).pixels( el ) ),
- r: round( borderWidths['r'].pixels( el ) + getLength( cs.paddingRight ).pixels( el ) ),
- b: round( borderWidths['b'].pixels( el ) + getLength( cs.paddingBottom ).pixels( el ) ),
- l: round( borderWidths['l'].pixels( el ) + getLength( cs.paddingLeft ).pixels( el ) )
- }, 2 )
+ 'path', this.getBoxPath(
+ round( borderWidths['t'].pixels( el ) + getLength( cs.paddingTop ).pixels( el ) ),
+ round( borderWidths['r'].pixels( el ) + getLength( cs.paddingRight ).pixels( el ) ),
+ round( borderWidths['b'].pixels( el ) + getLength( cs.paddingBottom ).pixels( el ) ),
+ round( borderWidths['l'].pixels( el ) + getLength( cs.paddingLeft ).pixels( el ) ),
+ 2
+ )
);
shape.setFillAttrs(
'type', 'frame',
@@ -95,59 +95,103 @@ PIE.merge(PIE.RendererBase, {
/**
* Return the VML path string for the element's background box, with corners rounded.
- * @param {Object.<{t:number, r:number, b:number, l:number}>} shrink - if present, specifies number of
- * pixels to shrink the box path inward from the element's four sides.
- * @param {number=} mult If specified, all coordinates will be multiplied by this number
+ * @param {number} shrinkT - number of pixels to shrink the box path inward from the element's top side.
+ * @param {number} shrinkR - number of pixels to shrink the box path inward from the element's right side.
+ * @param {number} shrinkB - number of pixels to shrink the box path inward from the element's bottom side.
+ * @param {number} shrinkL - number of pixels to shrink the box path inward from the element's left side.
+ * @param {number} mult All coordinates will be multiplied by this number
* @param {Object=} radii If specified, this will be used for the corner radii instead of the properties
* from this renderer's borderRadiusInfo object.
* @return {string} the VML path
*/
- getBoxPath: function( shrink, mult, radii ) {
- mult = mult || 1;
+ getBoxPath: function( shrinkT, shrinkR, shrinkB, shrinkL, mult, radii ) {
+ var str, coords, bounds, w, h,
+ M = Math, floor = M.floor, ceil = M.ceil;
- var r, str,
- bounds = this.boundsInfo.getBounds(),
- w = bounds.w * mult,
- h = bounds.h * mult,
- radInfo = this.styleInfos.borderRadiusInfo,
- floor = Math.floor, ceil = Math.ceil,
- shrinkT = shrink ? shrink.t * mult : 0,
- shrinkR = shrink ? shrink.r * mult : 0,
- shrinkB = shrink ? shrink.b * mult : 0,
- shrinkL = shrink ? shrink.l * mult : 0,
- tlX, tlY, trX, trY, brX, brY, blX, blY;
-
- if( radii || radInfo.isActive() ) {
- r = this.getRadiiPixels( radii || radInfo.getProps() );
-
- tlX = r.x['tl'] * mult;
- tlY = r.y['tl'] * mult;
- trX = r.x['tr'] * mult;
- trY = r.y['tr'] * mult;
- brX = r.x['br'] * mult;
- brY = r.y['br'] * mult;
- blX = r.x['bl'] * mult;
- blY = r.y['bl'] * mult;
-
- str = 'm' + floor( shrinkL ) + ',' + floor( tlY ) +
- 'qy' + floor( tlX ) + ',' + floor( shrinkT ) +
- 'l' + ceil( w - trX ) + ',' + floor( shrinkT ) +
- 'qx' + ceil( w - shrinkR ) + ',' + floor( trY ) +
- 'l' + ceil( w - shrinkR ) + ',' + ceil( h - brY ) +
- 'qy' + ceil( w - brX ) + ',' + ceil( h - shrinkB ) +
- 'l' + floor( blX ) + ',' + ceil( h - shrinkB ) +
- 'qx' + floor( shrinkL ) + ',' + ceil( h - blY ) + ' x e';
+ if( radii || this.styleInfos.borderRadiusInfo.isActive() ) {
+ // rounded box path
+ coords = this.getBoxPathCoords( shrinkT, shrinkR, shrinkB, shrinkL, mult, radii );
+ str = 'm' + coords[ 0 ] + ',' + coords[ 1 ] +
+ 'qy' + coords[ 2 ] + ',' + coords[ 3 ] +
+ 'l' + coords[ 4 ] + ',' + coords[ 5 ] +
+ 'qx' + coords[ 6 ] + ',' + coords[ 7 ] +
+ 'l' + coords[ 8 ] + ',' + coords[ 9 ] +
+ 'qy' + coords[ 10 ] + ',' + coords[ 11 ] +
+ 'l' + coords[ 12 ] + ',' + coords[ 13 ] +
+ 'qx' + coords[ 14 ] + ',' + coords[ 15 ] +
+ 'x';
} else {
- // simplified path for non-rounded box
+ // skip most of the heavy math for a non-rounded box
+ bounds = this.boundsInfo.getBounds();
+ w = bounds.w * mult;
+ h = bounds.h * mult;
+ shrinkT *= mult;
+ shrinkR *= mult;
+ shrinkB *= mult;
+ shrinkL *= mult;
str = 'm' + floor( shrinkL ) + ',' + floor( shrinkT ) +
'l' + ceil( w - shrinkR ) + ',' + floor( shrinkT ) +
'l' + ceil( w - shrinkR ) + ',' + ceil( h - shrinkB ) +
'l' + floor( shrinkL ) + ',' + ceil( h - shrinkB ) +
- 'xe';
+ 'x';
}
return str;
},
+ /**
+ * Return the VML coordinates for all the vertices in the rounded box path.
+ * @param {number} shrinkT - number of pixels to shrink the box path inward from the element's top side.
+ * @param {number} shrinkR - number of pixels to shrink the box path inward from the element's right side.
+ * @param {number} shrinkB - number of pixels to shrink the box path inward from the element's bottom side.
+ * @param {number} shrinkL - number of pixels to shrink the box path inward from the element's left side.
+ * @param {number=} mult If specified, all coordinates will be multiplied by this number
+ * @param {Object=} radii If specified, this will be used for the corner radii instead of the properties
+ * from this renderer's borderRadiusInfo object.
+ * @return {Array.<number>} all the coordinates going clockwise, starting with the top-left corner's lower vertex
+ */
+ getBoxPathCoords: function( shrinkT, shrinkR, shrinkB, shrinkL, mult, radii ) {
+ var bounds = this.boundsInfo.getBounds(),
+ w = bounds.w * mult,
+ h = bounds.h * mult,
+ M = Math,
+ floor = M.floor, ceil = M.ceil,
+ max = M.max, min = M.min,
+
+ r = this.getRadiiPixels( radii || this.styleInfos.borderRadiusInfo.getProps() ),
+ tlRadiusX = r.x['tl'] * mult,
+ tlRadiusY = r.y['tl'] * mult,
+ trRadiusX = r.x['tr'] * mult,
+ trRadiusY = r.y['tr'] * mult,
+ brRadiusX = r.x['br'] * mult,
+ brRadiusY = r.y['br'] * mult,
+ blRadiusX = r.x['bl'] * mult,
+ blRadiusY = r.y['bl'] * mult;
+
+ shrinkT *= mult;
+ shrinkR *= mult;
+ shrinkB *= mult;
+ shrinkL *= mult;
+
+ return [
+ floor( shrinkL ), // top-left lower x
+ floor( min( max( tlRadiusY, shrinkT ), h - shrinkB ) ), // top-left lower y
+ floor( min( max( tlRadiusX, shrinkL ), w - shrinkR ) ), // top-left upper x
+ floor( shrinkT ), // top-left upper y
+ ceil( max( shrinkL, w - max( trRadiusX, shrinkR ) ) ), // top-right upper x
+ floor( shrinkT ), // top-right upper y
+ ceil( w - shrinkR ), // top-right lower x
+ floor( min( max( trRadiusY, shrinkT ), h - shrinkB ) ), // top-right lower y
+ ceil( w - shrinkR ), // bottom-right upper x
+ ceil( max( shrinkT, h - max( brRadiusY, shrinkB ) ) ), // bottom-right upper y
+ ceil( max( shrinkL, w - max( brRadiusX, shrinkR ) ) ), // bottom-right lower x
+ ceil( h - shrinkB ), // bottom-right lower y
+ floor( min( max( blRadiusX, shrinkL ), w - shrinkR ) ), // bottom-left lower x
+ ceil( h - shrinkB ), // bottom-left lower y
+ floor( shrinkL ), // bottom-left upper x
+ ceil( max( shrinkT, h - max( blRadiusY, shrinkB ) ) ) // bottom-left upper y
+ ];
+ },
+
/**
* Hide the actual border of the element. In IE7 and up we can just set its color to transparent;
View
@@ -62,7 +62,7 @@ PIE.VmlShape = (function() {
VmlShape.prototype = {
behaviorStyle: 'behavior:url(#default#VML);',
defaultStyles: 'position:absolute;top:0px;left:0px;',
- defaultAttrs: 'coordorigin="1,1" ',
+ defaultAttrs: 'coordorigin="1,1" stroked="false" ',
tagName: 'shape',
mightBeRendered: 0,
@@ -74,7 +74,6 @@ PIE.VmlShape = (function() {
setAttrs: createSetter( '' ),
setStyles: createSetter( 'style' ),
setFillAttrs: createSetter( 'fill' ),
- setStrokeAttrs: createSetter( 'stroke' ),
setImageDataAttrs: createSetter( 'imagedata' ),
setSize: function( w, h ) {
@@ -104,8 +103,6 @@ PIE.VmlShape = (function() {
var m,
me = this,
tag = me.tagName,
- fill = 'fill',
- stroke = 'stroke',
tagStart = '<v:',
subElEnd = ' style="' + me.behaviorStyle + '" />';
@@ -135,7 +132,6 @@ PIE.VmlShape = (function() {
m.push( '>' );
pushElement( 'fill' );
- pushElement( 'stroke' );
pushElement( 'imagedata' );
m.push( '</v:' + tag + '>' );
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <title>Test cases for border-radius</title>
+
+ <style type="text/css">
+
+ html { -pie-load-path: "../build"; }
+
+ body {
+ font-size: 12px;
+ }
+
+ #tests {
+ padding: 2em;
+ }
+ #tests div {
+ width: 50%;
+ padding: 1.5em;
+ margin: 1em 0;
+ background: white;
+ white-space: pre;
+ behavior: url(../build/PIE.htc);
+ position: relative;
+ background: #CCF;
+ }
+
+ </style>
+
+</head>
+<body>
+
+ <h1>Tests for border-radius</h1>
+
+ <div id="tests">
+ <h2>Uniform</h2>
+ <div>border-radius: 10px;</div>
+ <div>border-radius: 1em;</div>
+ <div>border-radius: 20%;</div>
+
+ <h2>Different per corner</h2>
+ <div>border-radius: 5px 10px 15px 20px;</div>
+ <div>border-radius: 0.5em 1em 1.5em 2em;</div>
+ <div>border-radius: 10% 20% 30% 40%;</div>
+
+ <h2>Single corner</h2>
+ <div>border-radius: 20px 0 0 0;</div>
+ <div>border-radius: 0 20px 0 0;</div>
+ <div>border-radius: 0 0 20px 0;</div>
+ <div>border-radius: 0 0 0 20px;</div>
+
+ <h2>X and Y</h2>
+ <div>border-radius: 30px / 10px;</div>
+ <div>border-radius: 3em / 1em;</div>
+ <div>border-radius: 20% / 50%;</div>
+ <div>border-radius: 5px 10px 15px 20px / 20px 15px 10px 5px;</div>
+
+ <h2>Uniform with border</h2>
+ <div>border-radius: 10px; border: 5px solid #000;</div>
+ <div>border-radius: 10px; border: 5px dotted #000;</div>
+ <div>border-radius: 10px; border: 5px dashed #000;</div>
+ <div>border-radius: 10px; border: 5px double #000;</div>
+<!--
+ <div>border-radius: 10px; border: 5px groove #000;</div>
+ <div>border-radius: 10px; border: 5px ridge #000;</div>
+ <div>border-radius: 10px; border: 5px inset #000;</div>
+ <div>border-radius: 10px; border: 5px outset #000;</div>
+-->
+
+ <h2>Differing border widths</h2>
+ <div>border-radius: 10px; border: solid #000; border-width: 6px 9px 6px 3px;</div>
+ <div>border-radius: 10px; border: dotted #000; border-width: 6px 9px 6px 3px;</div>
+ <div>border-radius: 10px; border: dashed #000; border-width: 6px 9px 6px 3px;</div>
+ <div>border-radius: 10px; border: double #000; border-width: 6px 9px 6px 3px;</div>
+
+ <h2>Border thicker than radius</h2>
+ <div>border-radius: 10px; border: 20px solid #000;</div>
+ <div>border-radius: 10px; border: 20px dotted #000;</div>
+ <div>border-radius: 10px; border: 20px dashed #000;</div>
+ <div>border-radius: 10px; border: 20px double #000;</div>
+ <div>border-radius: 10px; border: solid #000; border-width: 3px 20px 9px 15px;</div>
+ <div>border-radius: 10px; border: dotted #000; border-width: 3px 20px 9px 15px;</div>
+ <div>border-radius: 10px; border: dashed #000; border-width: 3px 20px 9px 15px;</div>
+ <div>border-radius: 10px; border: double #000; border-width: 3px 20px 9px 15px;</div>
+
+ <h2>Zero-width borders</h2>
+ <div>border-radius: 10px; border: 20px solid #000; border-top-width: 0;</div>
+ <div>border-radius: 10px; border: 20px dotted #000; border-right-width: 0;</div>
+ <div>border-radius: 10px; border: 20px dashed #000; border-bottom-width: 0;</div>
+ <div>border-radius: 10px; border: 20px double #000; border-left-width: 0;</div>
+
+ <h2>Differing border colors</h2>
+ <div>border-radius: 10px; border: 20px solid; border-color: red green blue orange;</div>
+ <div>border-radius: 10px; border: 20px dotted; border-color: red green blue orange;</div>
+ <div>border-radius: 10px; border: 20px dashed; border-color: red green blue orange;</div>
+ <div>border-radius: 10px; border: 20px double; border-color: red green blue orange;</div>
+
+ <h2>Differing border colors and styles</h2>
+ <div>border-radius: 10px; border: 20px; border-color: red green blue orange; border-style: solid dotted dashed double;</div>
+
+ <h2>Differing border colors and widths</h2>
+ <div>border-radius: 10px; border: solid; border-color: red green blue orange; border-width: 3px 20px 9px 15px;</div>
+ <div>border-radius: 10px; border: dotted; border-color: red green blue orange; border-width: 3px 20px 9px 15px;</div>
+ <div>border-radius: 10px; border: dashed; border-color: red green blue orange; border-width: 3px 20px 9px 15px;</div>
+ <div>border-radius: 10px; border: double; border-color: red green blue orange; border-width: 3px 20px 9px 15px;</div>
+
+ <h2>Differing border colors, styles, and widths</h2>
+ <div>border-radius: 10px; border-style: solid dotted dashed double; border-color: red green blue orange; border-width: 3px 20px 9px 15px;</div>
+ </div>
+
+ <script type="text/javascript">
+ (function() {
+ var divs= document.getElementById("tests").getElementsByTagName("div"),
+ i = 0, len = divs.length, css;
+ for(; i < len; i++) {
+ divs[i].style.cssText += divs[i].firstChild.nodeValue;
+ }
+ })();
+ </script>
+
+
+</body>
+</html>

0 comments on commit 47dc9d8

Please sign in to comment.