1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
20 * Portions created by the Initial Developer are Copyright (C) 2008
21 * the Initial Developer. All Rights Reserved.
24 * Vladimir Vukicevic <vladimir@pobox.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsStyleConsts.h"
41 #include "nsPresContext.h"
46 #include "nsIViewManager.h"
47 #include "nsIPresShell.h"
48 #include "nsFrameManager.h"
49 #include "nsStyleContext.h"
50 #include "nsGkAtoms.h"
51 #include "nsCSSAnonBoxes.h"
52 #include "nsTransform2D.h"
53 #include "nsIDeviceContext.h"
54 #include "nsIContent.h"
55 #include "nsIDocument.h"
56 #include "nsIScrollableFrame.h"
57 #include "imgIRequest.h"
58 #include "imgIContainer.h"
59 #include "gfxIImageFrame.h"
60 #include "nsCSSRendering.h"
61 #include "nsCSSColorUtils.h"
63 #include "nsThemeConstants.h"
64 #include "nsIServiceManager.h"
65 #include "nsIHTMLDocument.h"
66 #include "nsLayoutUtils.h"
67 #include "nsINameSpaceManager.h"
68 #include "nsBlockFrame.h"
70 #include "gfxContext.h"
72 #include "nsCSSRenderingBorders.h"
75 * nsCSSRendering::PaintBorder
76 * nsCSSRendering::PaintOutline
80 * |- separate corners?
83 * -> can border be drawn in 1 pass? (e.g., solid border same color all around)
84 * |- DrawBorderSides with all 4 sides
85 * -> more than 1 pass?
87 * |- clip to DoCornerClipSubPath
89 * |- for each side adjacent to corner
90 * |- clip to DoSideClipSubPath
91 * |- DrawBorderSides with one side
94 * |- DoSideClipWithoutCornersSubPath
95 * |- DrawDashedSide || DrawBorderSides with one side
98 static void ComputeBorderCornerDimensions(const gfxRect
& aOuterRect
,
99 const gfxRect
& aInnerRect
,
100 const gfxCornerSizes
& aRadii
,
101 gfxCornerSizes
*aDimsResult
);
103 static void ComputeInnerRadii(const gfxCornerSizes
& radii
,
104 const gfxFloat
*borderSizes
,
105 gfxCornerSizes
*innerRadii
);
107 // given a side index, get the previous and next side index
108 #define NEXT_SIDE(_s) (((_s) + 1) & 3)
109 #define PREV_SIDE(_s) (((_s) + 3) & 3)
111 // from the given base color and the background color, turn
112 // color into a color for the given border pattern style
113 static gfxRGBA
MakeBorderColor(const gfxRGBA
& aColor
,
114 const gfxRGBA
& aBackgroundColor
,
115 BorderColorStyle aBorderColorStyle
);
118 // Given a line index (an index starting from the outside of the
119 // border going inwards) and an array of line styles, calculate the
120 // color that that stripe of the border should be rendered in.
121 static gfxRGBA
ComputeColorForLine(PRUint32 aLineIndex
,
122 const BorderColorStyle
* aBorderColorStyle
,
123 PRUint32 aBorderColorStyleCount
,
124 nscolor aBorderColor
,
125 nscolor aBackgroundColor
);
127 static gfxRGBA
ComputeCompositeColorForLine(PRUint32 aLineIndex
,
128 const nsBorderColors
* aBorderColors
);
130 // little helper function to check if the array of 4 floats given are
131 // equal to the given value
133 CheckFourFloatsEqual(const gfxFloat
*vals
, gfxFloat k
)
135 return (vals
[0] == k
&&
142 IsZeroSize(const gfxSize
& sz
) {
143 return sz
.width
== 0.0 || sz
.height
== 0.0;
147 AllCornersZeroSize(const gfxCornerSizes
& corners
) {
148 return IsZeroSize(corners
[0]) &&
149 IsZeroSize(corners
[1]) &&
150 IsZeroSize(corners
[2]) &&
151 IsZeroSize(corners
[3]);
155 // Normal solid square corner. Will be rectangular, the size of the
156 // adjacent sides. If the corner has a border radius, the corner
157 // will always be solid, since we don't do dotted/dashed etc.
160 // Paint the corner in whatever style is not dotted/dashed of the
164 // Paint the corner as a dot, the size of the bigger of the adjacent
169 nsCSSBorderRenderer::nsCSSBorderRenderer(PRInt32 aAppUnitsPerPixel
,
170 gfxContext
* aDestContext
,
172 const PRUint8
* aBorderStyles
,
173 const gfxFloat
* aBorderWidths
,
174 gfxCornerSizes
& aBorderRadii
,
175 const nscolor
* aBorderColors
,
176 nsBorderColors
* const* aCompositeColors
,
178 nscolor aBackgroundColor
)
179 : mAUPP(aAppUnitsPerPixel
),
180 mContext(aDestContext
),
181 mOuterRect(aOuterRect
),
182 mBorderStyles(aBorderStyles
),
183 mBorderWidths(aBorderWidths
),
184 mBorderRadii(aBorderRadii
),
185 mBorderColors(aBorderColors
),
186 mCompositeColors(aCompositeColors
),
187 mSkipSides(aSkipSides
),
188 mBackgroundColor(aBackgroundColor
)
190 if (!mCompositeColors
) {
191 static nsBorderColors
* const noColors
[4] = { NULL
};
192 mCompositeColors
= &noColors
[0];
195 mInnerRect
= mOuterRect
;
196 mInnerRect
.Inset(mBorderWidths
[0], mBorderWidths
[1], mBorderWidths
[2], mBorderWidths
[3]);
198 ComputeBorderCornerDimensions(mOuterRect
, mInnerRect
, mBorderRadii
, &mBorderCornerDimensions
);
200 mOneUnitBorder
= CheckFourFloatsEqual(mBorderWidths
, 1.0);
201 mNoBorderRadius
= AllCornersZeroSize(mBorderRadii
);
205 ComputeInnerRadii(const gfxCornerSizes
& aRadii
,
206 const gfxFloat
*aBorderSizes
,
207 gfxCornerSizes
*aInnerRadiiRet
)
209 gfxCornerSizes
& iRadii
= *aInnerRadiiRet
;
211 iRadii
[C_TL
].width
= PR_MAX(0.0, aRadii
[C_TL
].width
- aBorderSizes
[NS_SIDE_LEFT
]);
212 iRadii
[C_TL
].height
= PR_MAX(0.0, aRadii
[C_TL
].height
- aBorderSizes
[NS_SIDE_TOP
]);
214 iRadii
[C_TR
].width
= PR_MAX(0.0, aRadii
[C_TR
].width
- aBorderSizes
[NS_SIDE_RIGHT
]);
215 iRadii
[C_TR
].height
= PR_MAX(0.0, aRadii
[C_TR
].height
- aBorderSizes
[NS_SIDE_TOP
]);
217 iRadii
[C_BR
].width
= PR_MAX(0.0, aRadii
[C_BR
].width
- aBorderSizes
[NS_SIDE_RIGHT
]);
218 iRadii
[C_BR
].height
= PR_MAX(0.0, aRadii
[C_BR
].height
- aBorderSizes
[NS_SIDE_BOTTOM
]);
220 iRadii
[C_BL
].width
= PR_MAX(0.0, aRadii
[C_BL
].width
- aBorderSizes
[NS_SIDE_LEFT
]);
221 iRadii
[C_BL
].height
= PR_MAX(0.0, aRadii
[C_BL
].height
- aBorderSizes
[NS_SIDE_BOTTOM
]);
225 ComputeBorderCornerDimensions(const gfxRect
& aOuterRect
,
226 const gfxRect
& aInnerRect
,
227 const gfxCornerSizes
& aRadii
,
228 gfxCornerSizes
*aDimsRet
)
230 gfxFloat topWidth
= aInnerRect
.pos
.y
- aOuterRect
.pos
.y
;
231 gfxFloat leftWidth
= aInnerRect
.pos
.x
- aOuterRect
.pos
.x
;
232 gfxFloat rightWidth
= aOuterRect
.size
.width
- aInnerRect
.size
.width
- leftWidth
;
233 gfxFloat bottomWidth
= aOuterRect
.size
.height
- aInnerRect
.size
.height
- topWidth
;
235 if (AllCornersZeroSize(aRadii
)) {
236 // These will always be in pixel units from CSS
237 (*aDimsRet
)[C_TL
] = gfxSize(leftWidth
, topWidth
);
238 (*aDimsRet
)[C_TR
] = gfxSize(rightWidth
, topWidth
);
239 (*aDimsRet
)[C_BR
] = gfxSize(rightWidth
, bottomWidth
);
240 (*aDimsRet
)[C_BL
] = gfxSize(leftWidth
, bottomWidth
);
242 // Always round up to whole pixels for the corners; it's safe to
243 // make the corners bigger than necessary, and this way we ensure
244 // that we avoid seams.
245 (*aDimsRet
)[C_TL
] = gfxSize(ceil(PR_MAX(leftWidth
, aRadii
[C_TL
].width
)),
246 ceil(PR_MAX(topWidth
, aRadii
[C_TL
].height
)));
247 (*aDimsRet
)[C_TR
] = gfxSize(ceil(PR_MAX(rightWidth
, aRadii
[C_TR
].width
)),
248 ceil(PR_MAX(topWidth
, aRadii
[C_TR
].height
)));
249 (*aDimsRet
)[C_BR
] = gfxSize(ceil(PR_MAX(rightWidth
, aRadii
[C_BR
].width
)),
250 ceil(PR_MAX(bottomWidth
, aRadii
[C_BR
].height
)));
251 (*aDimsRet
)[C_BL
] = gfxSize(ceil(PR_MAX(leftWidth
, aRadii
[C_BL
].width
)),
252 ceil(PR_MAX(bottomWidth
, aRadii
[C_BL
].height
)));
257 nsCSSBorderRenderer::AreBorderSideFinalStylesSame(PRUint8 aSides
)
259 NS_ASSERTION(aSides
!= 0 && (aSides
& ~SIDE_BITS_ALL
) == 0,
260 "AreBorderSidesSame: invalid whichSides!");
262 /* First check if the specified styles and colors are the same for all sides */
264 NS_FOR_CSS_SIDES (i
) {
265 if (firstStyle
== i
) {
266 if (((1 << i
) & aSides
) == 0)
271 if (((1 << i
) & aSides
) == 0) {
275 if (mBorderStyles
[firstStyle
] != mBorderStyles
[i
] ||
276 mBorderColors
[firstStyle
] != mBorderColors
[i
] ||
277 !nsBorderColors::Equal(mCompositeColors
[firstStyle
],
278 mCompositeColors
[i
]))
282 /* Then if it's one of the two-tone styles and we're not
283 * just comparing the TL or BR sides */
284 switch (mBorderStyles
[firstStyle
]) {
285 case NS_STYLE_BORDER_STYLE_GROOVE
:
286 case NS_STYLE_BORDER_STYLE_RIDGE
:
287 case NS_STYLE_BORDER_STYLE_INSET
:
288 case NS_STYLE_BORDER_STYLE_OUTSET
:
289 return ((aSides
& ~(SIDE_BIT_TOP
| SIDE_BIT_LEFT
)) == 0 ||
290 (aSides
& ~(SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
)) == 0);
297 nsCSSBorderRenderer::IsSolidCornerStyle(PRUint8 aStyle
, gfxCorner::Corner aCorner
)
300 case NS_STYLE_BORDER_STYLE_DOTTED
:
301 case NS_STYLE_BORDER_STYLE_DASHED
:
302 case NS_STYLE_BORDER_STYLE_SOLID
:
305 case NS_STYLE_BORDER_STYLE_INSET
:
306 case NS_STYLE_BORDER_STYLE_OUTSET
:
307 return (aCorner
== gfxCorner::TOP_LEFT
|| aCorner
== gfxCorner::BOTTOM_RIGHT
);
309 case NS_STYLE_BORDER_STYLE_GROOVE
:
310 case NS_STYLE_BORDER_STYLE_RIDGE
:
311 return mOneUnitBorder
&& (aCorner
== gfxCorner::TOP_LEFT
|| aCorner
== gfxCorner::BOTTOM_RIGHT
);
313 case NS_STYLE_BORDER_STYLE_DOUBLE
:
314 return mOneUnitBorder
;
322 nsCSSBorderRenderer::BorderColorStyleForSolidCorner(PRUint8 aStyle
, gfxCorner::Corner aCorner
)
324 // note that this function assumes that the corner is already solid,
325 // as per the earlier function
327 case NS_STYLE_BORDER_STYLE_DOTTED
:
328 case NS_STYLE_BORDER_STYLE_DASHED
:
329 case NS_STYLE_BORDER_STYLE_SOLID
:
330 case NS_STYLE_BORDER_STYLE_DOUBLE
:
331 return BorderColorStyleSolid
;
333 case NS_STYLE_BORDER_STYLE_INSET
:
334 case NS_STYLE_BORDER_STYLE_GROOVE
:
335 if (aCorner
== gfxCorner::TOP_LEFT
)
336 return BorderColorStyleDark
;
337 else if (aCorner
== gfxCorner::BOTTOM_RIGHT
)
338 return BorderColorStyleLight
;
341 case NS_STYLE_BORDER_STYLE_OUTSET
:
342 case NS_STYLE_BORDER_STYLE_RIDGE
:
343 if (aCorner
== gfxCorner::TOP_LEFT
)
344 return BorderColorStyleLight
;
345 else if (aCorner
== gfxCorner::BOTTOM_RIGHT
)
346 return BorderColorStyleDark
;
350 return BorderColorStyleNone
;
354 nsCSSBorderRenderer::DoCornerSubPath(PRUint8 aCorner
)
356 gfxPoint
offset(0.0, 0.0);
358 if (aCorner
== C_TR
|| aCorner
== C_BR
)
359 offset
.x
= mOuterRect
.size
.width
- mBorderCornerDimensions
[aCorner
].width
;
360 if (aCorner
== C_BR
|| aCorner
== C_BL
)
361 offset
.y
= mOuterRect
.size
.height
- mBorderCornerDimensions
[aCorner
].height
;
363 mContext
->Rectangle(gfxRect(mOuterRect
.pos
+ offset
,
364 mBorderCornerDimensions
[aCorner
]));
368 nsCSSBorderRenderer::DoSideClipWithoutCornersSubPath(PRUint8 aSide
)
370 gfxPoint
offset(0.0, 0.0);
372 // The offset from the outside rect to the start of this side's
373 // box. For the top and bottom sides, the height of the box
374 // must be the border height; the x start must take into account
375 // the corner size (which may be bigger than the right or left
376 // side's width). The same applies to the right and left sides.
377 if (aSide
== NS_SIDE_TOP
) {
378 offset
.x
= mBorderCornerDimensions
[C_TL
].width
;
379 } else if (aSide
== NS_SIDE_RIGHT
) {
380 offset
.x
= mOuterRect
.size
.width
- mBorderWidths
[NS_SIDE_RIGHT
];
381 offset
.y
= mBorderCornerDimensions
[C_TR
].height
;
382 } else if (aSide
== NS_SIDE_BOTTOM
) {
383 offset
.x
= mBorderCornerDimensions
[C_BL
].width
;
384 offset
.y
= mOuterRect
.size
.height
- mBorderWidths
[NS_SIDE_BOTTOM
];
385 } else if (aSide
== NS_SIDE_LEFT
) {
386 offset
.y
= mBorderCornerDimensions
[C_TL
].height
;
389 // The sum of the width & height of the corners adjacent to the
390 // side. This relies on the relationship between side indexing and
391 // corner indexing; that is, 0 == SIDE_TOP and 0 == CORNER_TOP_LEFT,
392 // with both proceeding clockwise.
393 gfxSize sideCornerSum
= mBorderCornerDimensions
[aSide
] + mBorderCornerDimensions
[NEXT_SIDE(aSide
)];
394 gfxRect
rect(mOuterRect
.pos
+ offset
,
395 mOuterRect
.size
- sideCornerSum
);
397 if (aSide
== NS_SIDE_TOP
|| aSide
== NS_SIDE_BOTTOM
)
398 rect
.size
.height
= mBorderWidths
[aSide
];
400 rect
.size
.width
= mBorderWidths
[aSide
];
402 mContext
->Rectangle(rect
);
405 // The side border type and the adjacent border types are
406 // examined and one of the different types of clipping (listed
407 // below) is selected.
410 // clip to the trapezoid formed by the corners of the
411 // inner and outer rectangles for the given side
414 // clip to the trapezoid formed by the outer rectangle
415 // corners and the center of the region, making sure
416 // that diagonal lines all go directly from the outside
417 // corner to the inside corner, but that they then continue on
420 // This is needed for correctly clipping rounded borders,
421 // which might extend past the SIDE_CLIP_TRAPEZOID trap.
422 SIDE_CLIP_TRAPEZOID_FULL
,
424 // clip to the rectangle formed by the given side; a specific
425 // overlap algorithm is used; see the function for details.
426 // this is currently used for dashing.
430 // Given three points, p0, p1, and midPoint, if p0 and p1 do not form
431 // a horizontal or vertical line move p1 to the point nearest the
432 // midpoint, while maintaing the slope of the line.
434 MaybeMoveToMidPoint(gfxPoint
& aP0
, gfxPoint
& aP1
, const gfxPoint
& aMidPoint
)
436 gfxPoint ps
= aP1
- aP0
;
438 if (ps
.x
!= 0.0 && ps
.y
!= 0.0) {
439 gfxFloat k
= PR_MIN((aMidPoint
.x
- aP0
.x
) / ps
.x
,
440 (aMidPoint
.y
- aP1
.y
) / ps
.y
);
446 nsCSSBorderRenderer::DoSideClipSubPath(PRUint8 aSide
)
448 // the clip proceeds clockwise from the top left corner;
449 // so "start" in each case is the start of the region from that side.
451 // the final path will be formed like:
456 // that is, the second point will always be on the inside
461 #define IS_DASHED_OR_DOTTED(_s) ((_s) == NS_STYLE_BORDER_STYLE_DASHED || (_s) == NS_STYLE_BORDER_STYLE_DOTTED)
462 PRBool isDashed
= IS_DASHED_OR_DOTTED(mBorderStyles
[aSide
]);
463 PRBool startIsDashed
= IS_DASHED_OR_DOTTED(mBorderStyles
[PREV_SIDE(aSide
)]);
464 PRBool endIsDashed
= IS_DASHED_OR_DOTTED(mBorderStyles
[NEXT_SIDE(aSide
)]);
465 #undef IS_DASHED_OR_DOTTED
467 SideClipType startType
= SIDE_CLIP_TRAPEZOID
;
468 SideClipType endType
= SIDE_CLIP_TRAPEZOID
;
470 if (!IsZeroSize(mBorderRadii
[aSide
]))
471 startType
= SIDE_CLIP_TRAPEZOID_FULL
;
472 else if (startIsDashed
&& isDashed
)
473 startType
= SIDE_CLIP_RECTANGLE
;
475 if (!IsZeroSize(mBorderRadii
[NEXT_SIDE(aSide
)]))
476 endType
= SIDE_CLIP_TRAPEZOID_FULL
;
477 else if (endIsDashed
&& isDashed
)
478 endType
= SIDE_CLIP_RECTANGLE
;
480 gfxPoint midPoint
= mInnerRect
.pos
+ mInnerRect
.size
/ 2.0;
482 start
[0] = mOuterRect
.Corner(aSide
);
483 start
[1] = mInnerRect
.Corner(aSide
);
485 end
[0] = mOuterRect
.Corner(NEXT_SIDE(aSide
));
486 end
[1] = mInnerRect
.Corner(NEXT_SIDE(aSide
));
488 if (startType
== SIDE_CLIP_TRAPEZOID_FULL
) {
489 MaybeMoveToMidPoint(start
[0], start
[1], midPoint
);
490 } else if (startType
== SIDE_CLIP_RECTANGLE
) {
491 if (aSide
== NS_SIDE_TOP
|| aSide
== NS_SIDE_BOTTOM
)
492 start
[1] = gfxPoint(mOuterRect
.Corner(aSide
).x
, mInnerRect
.Corner(aSide
).y
);
494 start
[1] = gfxPoint(mInnerRect
.Corner(aSide
).x
, mOuterRect
.Corner(aSide
).y
);
497 if (endType
== SIDE_CLIP_TRAPEZOID_FULL
) {
498 MaybeMoveToMidPoint(end
[0], end
[1], midPoint
);
499 } else if (endType
== SIDE_CLIP_RECTANGLE
) {
500 if (aSide
== NS_SIDE_TOP
|| aSide
== NS_SIDE_BOTTOM
)
501 end
[0] = gfxPoint(mInnerRect
.Corner(NEXT_SIDE(aSide
)).x
, mOuterRect
.Corner(NEXT_SIDE(aSide
)).y
);
503 end
[0] = gfxPoint(mOuterRect
.Corner(NEXT_SIDE(aSide
)).x
, mInnerRect
.Corner(NEXT_SIDE(aSide
)).y
);
506 mContext
->MoveTo(start
[0]);
507 mContext
->LineTo(end
[0]);
508 mContext
->LineTo(end
[1]);
509 mContext
->LineTo(start
[1]);
510 mContext
->ClosePath();
514 nsCSSBorderRenderer::FillSolidBorder(const gfxRect
& aOuterRect
,
515 const gfxRect
& aInnerRect
,
516 const gfxCornerSizes
& aBorderRadii
,
517 const gfxFloat
*aBorderSizes
,
519 const gfxRGBA
& aColor
)
521 mContext
->SetColor(aColor
);
522 // Note that this function is allowed to draw more than just the
525 // If we have a border radius, do full rounded rectangles
526 // and fill, regardless of what sides we're asked to draw.
527 if (!AllCornersZeroSize(aBorderRadii
)) {
528 gfxCornerSizes innerRadii
;
529 ComputeInnerRadii(aBorderRadii
, aBorderSizes
, &innerRadii
);
533 // do the outer border
534 mContext
->RoundedRectangle(aOuterRect
, aBorderRadii
, PR_TRUE
);
536 // then do the inner border CCW
537 mContext
->RoundedRectangle(aInnerRect
, innerRadii
, PR_FALSE
);
544 // If we're asked to draw all sides of an equal-sized border,
545 // stroking is fastest. This is a fairly common path, but partial
546 // sides is probably second in the list -- there are a bunch of
547 // common border styles, such as inset and outset, that are
548 // top-left/bottom-right split.
549 if (aSides
== SIDE_BITS_ALL
&&
550 CheckFourFloatsEqual(aBorderSizes
, aBorderSizes
[0]))
552 gfxRect
r(aOuterRect
);
553 r
.Inset(aBorderSizes
[0] / 2.0);
554 mContext
->SetLineWidth(aBorderSizes
[0]);
557 mContext
->Rectangle(r
);
563 // Otherwise, we have unequal sized borders or we're only
564 // drawing some sides; create rectangles for each side
569 // compute base rects for each side
570 if (aSides
& SIDE_BIT_TOP
) {
571 r
[NS_SIDE_TOP
].pos
= aOuterRect
.TopLeft();
572 r
[NS_SIDE_TOP
].size
.width
= aOuterRect
.size
.width
;
573 r
[NS_SIDE_TOP
].size
.height
= aBorderSizes
[NS_SIDE_TOP
];
576 if (aSides
& SIDE_BIT_BOTTOM
) {
577 r
[NS_SIDE_BOTTOM
].pos
= aOuterRect
.BottomLeft();
578 r
[NS_SIDE_BOTTOM
].pos
.y
-= aBorderSizes
[NS_SIDE_BOTTOM
];
579 r
[NS_SIDE_BOTTOM
].size
.width
= aOuterRect
.size
.width
;
580 r
[NS_SIDE_BOTTOM
].size
.height
= aBorderSizes
[NS_SIDE_BOTTOM
];
583 if (aSides
& SIDE_BIT_LEFT
) {
584 r
[NS_SIDE_LEFT
].pos
= aOuterRect
.TopLeft();
585 r
[NS_SIDE_LEFT
].size
.width
= aBorderSizes
[NS_SIDE_LEFT
];
586 r
[NS_SIDE_LEFT
].size
.height
= aOuterRect
.size
.height
;
589 if (aSides
& SIDE_BIT_RIGHT
) {
590 r
[NS_SIDE_RIGHT
].pos
= aOuterRect
.TopRight();
591 r
[NS_SIDE_RIGHT
].pos
.x
-= aBorderSizes
[NS_SIDE_RIGHT
];
592 r
[NS_SIDE_RIGHT
].size
.width
= aBorderSizes
[NS_SIDE_RIGHT
];
593 r
[NS_SIDE_RIGHT
].size
.height
= aOuterRect
.size
.height
;
596 // If two sides meet at a corner that we're rendering, then
597 // make sure that we adjust one of the sides to avoid overlap.
598 // This is especially important in the case of colors with
601 if ((aSides
& (SIDE_BIT_TOP
| SIDE_BIT_LEFT
)) == (SIDE_BIT_TOP
| SIDE_BIT_LEFT
)) {
602 // adjust the left's top down a bit
603 r
[NS_SIDE_LEFT
].pos
.y
+= aBorderSizes
[NS_SIDE_TOP
];
604 r
[NS_SIDE_LEFT
].size
.height
-= aBorderSizes
[NS_SIDE_TOP
];
607 if ((aSides
& (SIDE_BIT_TOP
| SIDE_BIT_RIGHT
)) == (SIDE_BIT_TOP
| SIDE_BIT_RIGHT
)) {
608 // adjust the top's left a bit
609 r
[NS_SIDE_TOP
].size
.width
-= aBorderSizes
[NS_SIDE_RIGHT
];
612 if ((aSides
& (SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
)) == (SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
)) {
613 // adjust the right's bottom a bit
614 r
[NS_SIDE_RIGHT
].size
.height
-= aBorderSizes
[NS_SIDE_BOTTOM
];
617 if ((aSides
& (SIDE_BIT_BOTTOM
| SIDE_BIT_LEFT
)) == (SIDE_BIT_BOTTOM
| SIDE_BIT_LEFT
)) {
618 // adjust the bottom's left a bit
619 r
[NS_SIDE_BOTTOM
].pos
.x
+= aBorderSizes
[NS_SIDE_LEFT
];
620 r
[NS_SIDE_BOTTOM
].size
.width
-= aBorderSizes
[NS_SIDE_LEFT
];
623 // Filling these one by one is faster than filling them all at once.
624 for (PRUint32 i
= 0; i
< 4; i
++) {
625 if (aSides
& (1 << i
)) {
627 mContext
->Rectangle(r
[i
]);
634 MakeBorderColor(const gfxRGBA
& aColor
, const gfxRGBA
& aBackgroundColor
, BorderColorStyle aBorderColorStyle
)
639 switch (aBorderColorStyle
) {
640 case BorderColorStyleNone
:
641 return gfxRGBA(0.0, 0.0, 0.0, 0.0);
643 case BorderColorStyleLight
:
646 case BorderColorStyleDark
:
647 NS_GetSpecial3DColors(colors
, aBackgroundColor
.Packed(), aColor
.Packed());
648 return gfxRGBA(colors
[k
]);
650 case BorderColorStyleSolid
:
657 ComputeColorForLine(PRUint32 aLineIndex
,
658 const BorderColorStyle
* aBorderColorStyle
,
659 PRUint32 aBorderColorStyleCount
,
660 nscolor aBorderColor
,
661 nscolor aBackgroundColor
)
663 NS_ASSERTION(aLineIndex
< aBorderColorStyleCount
, "Invalid lineIndex given");
665 return MakeBorderColor(gfxRGBA(aBorderColor
), gfxRGBA(aBackgroundColor
), aBorderColorStyle
[aLineIndex
]);
669 ComputeCompositeColorForLine(PRUint32 aLineIndex
,
670 const nsBorderColors
* aBorderColors
)
672 while (aLineIndex
-- && aBorderColors
->mNext
)
673 aBorderColors
= aBorderColors
->mNext
;
675 return gfxRGBA(aBorderColors
->mColor
);
679 nsCSSBorderRenderer::DrawBorderSidesCompositeColors(PRIntn aSides
, const nsBorderColors
*aCompositeColors
)
681 gfxCornerSizes radii
= mBorderRadii
;
683 // the generic composite colors path; each border is 1px in size
684 gfxRect soRect
= mOuterRect
;
686 gfxFloat maxBorderWidth
= 0;
687 NS_FOR_CSS_SIDES (i
) {
688 maxBorderWidth
= PR_MAX(maxBorderWidth
, mBorderWidths
[i
]);
691 gfxFloat fakeBorderSizes
[4];
696 gfxPoint itl
= mInnerRect
.TopLeft();
697 gfxPoint ibr
= mInnerRect
.BottomRight();
699 for (PRUint32 i
= 0; i
< PRUint32(maxBorderWidth
); i
++) {
700 lineColor
= ComputeCompositeColorForLine(i
, aCompositeColors
);
703 siRect
.Inset(1.0, 1.0, 1.0, 1.0);
705 // now cap the rects to the real mInnerRect
706 tl
= siRect
.TopLeft();
707 br
= siRect
.BottomRight();
709 tl
.x
= PR_MIN(tl
.x
, itl
.x
);
710 tl
.y
= PR_MIN(tl
.y
, itl
.y
);
712 br
.x
= PR_MAX(br
.x
, ibr
.x
);
713 br
.y
= PR_MAX(br
.y
, ibr
.y
);
716 siRect
.size
.width
= br
.x
- tl
.x
;
717 siRect
.size
.height
= br
.y
- tl
.y
;
719 fakeBorderSizes
[NS_SIDE_TOP
] = siRect
.TopLeft().y
- soRect
.TopLeft().y
;
720 fakeBorderSizes
[NS_SIDE_RIGHT
] = soRect
.TopRight().x
- siRect
.TopRight().x
;
721 fakeBorderSizes
[NS_SIDE_BOTTOM
] = soRect
.BottomRight().y
- siRect
.BottomRight().y
;
722 fakeBorderSizes
[NS_SIDE_LEFT
] = siRect
.BottomLeft().x
- soRect
.BottomLeft().x
;
724 FillSolidBorder(soRect
, siRect
, radii
, fakeBorderSizes
, aSides
, lineColor
);
728 ComputeInnerRadii(radii
, fakeBorderSizes
, &radii
);
733 nsCSSBorderRenderer::DrawBorderSides(PRIntn aSides
)
735 if (aSides
== 0 || (aSides
& ~SIDE_BITS_ALL
) != 0) {
736 NS_WARNING("DrawBorderSides: invalid sides!");
740 PRUint8 borderRenderStyle
;
741 nscolor borderRenderColor
;
742 const nsBorderColors
*compositeColors
= nsnull
;
744 PRUint32 borderColorStyleCount
= 0;
745 BorderColorStyle borderColorStyleTopLeft
[3], borderColorStyleBottomRight
[3];
746 BorderColorStyle
*borderColorStyle
= nsnull
;
748 NS_FOR_CSS_SIDES (i
) {
749 if ((aSides
& (1 << i
)) == 0)
751 borderRenderStyle
= mBorderStyles
[i
];
752 borderRenderColor
= mBorderColors
[i
];
753 compositeColors
= mCompositeColors
[i
];
757 if (borderRenderStyle
== NS_STYLE_BORDER_STYLE_NONE
||
758 borderRenderStyle
== NS_STYLE_BORDER_STYLE_HIDDEN
)
761 // -moz-border-colors is a hack; if we have it for a border, then
762 // it's always drawn solid, and each color is given 1px. The last
763 // color is used for the remainder of the border's size. Just
764 // hand off to another function to do all that.
765 if (compositeColors
) {
766 DrawBorderSidesCompositeColors(aSides
, compositeColors
);
770 // We're not doing compositeColors, so we can calculate the
771 // borderColorStyle based on the specified style. The
772 // borderColorStyle array goes from the outer to the inner style.
774 // If the border width is 1, we need to change the borderRenderStyle
775 // a bit to make sure that we get the right colors -- e.g. 'ridge'
776 // with a 1px border needs to look like solid, not like 'outset'.
777 if (mOneUnitBorder
&&
778 (borderRenderStyle
== NS_STYLE_BORDER_STYLE_RIDGE
||
779 borderRenderStyle
== NS_STYLE_BORDER_STYLE_GROOVE
||
780 borderRenderStyle
== NS_STYLE_BORDER_STYLE_DOUBLE
))
781 borderRenderStyle
= NS_STYLE_BORDER_STYLE_SOLID
;
783 switch (borderRenderStyle
) {
784 case NS_STYLE_BORDER_STYLE_SOLID
:
785 case NS_STYLE_BORDER_STYLE_DASHED
:
786 case NS_STYLE_BORDER_STYLE_DOTTED
:
787 borderColorStyleTopLeft
[0] = BorderColorStyleSolid
;
789 borderColorStyleBottomRight
[0] = BorderColorStyleSolid
;
791 borderColorStyleCount
= 1;
794 case NS_STYLE_BORDER_STYLE_GROOVE
:
795 borderColorStyleTopLeft
[0] = BorderColorStyleDark
;
796 borderColorStyleTopLeft
[1] = BorderColorStyleLight
;
798 borderColorStyleBottomRight
[0] = BorderColorStyleLight
;
799 borderColorStyleBottomRight
[1] = BorderColorStyleDark
;
801 borderColorStyleCount
= 2;
804 case NS_STYLE_BORDER_STYLE_RIDGE
:
805 borderColorStyleTopLeft
[0] = BorderColorStyleLight
;
806 borderColorStyleTopLeft
[1] = BorderColorStyleDark
;
808 borderColorStyleBottomRight
[0] = BorderColorStyleDark
;
809 borderColorStyleBottomRight
[1] = BorderColorStyleLight
;
811 borderColorStyleCount
= 2;
814 case NS_STYLE_BORDER_STYLE_DOUBLE
:
815 borderColorStyleTopLeft
[0] = BorderColorStyleSolid
;
816 borderColorStyleTopLeft
[1] = BorderColorStyleNone
;
817 borderColorStyleTopLeft
[2] = BorderColorStyleSolid
;
819 borderColorStyleBottomRight
[0] = BorderColorStyleSolid
;
820 borderColorStyleBottomRight
[1] = BorderColorStyleNone
;
821 borderColorStyleBottomRight
[2] = BorderColorStyleSolid
;
823 borderColorStyleCount
= 3;
826 case NS_STYLE_BORDER_STYLE_INSET
:
827 borderColorStyleTopLeft
[0] = BorderColorStyleDark
;
828 borderColorStyleBottomRight
[0] = BorderColorStyleLight
;
830 borderColorStyleCount
= 1;
833 case NS_STYLE_BORDER_STYLE_OUTSET
:
834 borderColorStyleTopLeft
[0] = BorderColorStyleLight
;
835 borderColorStyleBottomRight
[0] = BorderColorStyleDark
;
837 borderColorStyleCount
= 1;
841 NS_NOTREACHED("Unhandled border style!!");
845 // The only way to get to here is by having a
846 // borderColorStyleCount < 1 or > 3; this should never happen,
847 // since -moz-border-colors doesn't get handled here.
848 NS_ASSERTION(borderColorStyleCount
> 0 && borderColorStyleCount
< 4,
849 "Non-border-colors case with borderColorStyleCount < 1 or > 3; what happened?");
851 // The caller should never give us anything with a mix
852 // of TL/BR if the border style would require a
854 if (aSides
& (SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
))
855 borderColorStyle
= borderColorStyleBottomRight
;
857 borderColorStyle
= borderColorStyleTopLeft
;
859 // Distribute the border across the available space.
860 gfxFloat borderWidths
[3][4];
862 if (borderColorStyleCount
== 1) {
863 NS_FOR_CSS_SIDES (i
) {
864 borderWidths
[0][i
] = mBorderWidths
[i
];
866 } else if (borderColorStyleCount
== 2) {
867 // with 2 color styles, any extra pixel goes to the outside
868 NS_FOR_CSS_SIDES (i
) {
869 borderWidths
[0][i
] = PRInt32(mBorderWidths
[i
]) / 2 + PRInt32(mBorderWidths
[i
]) % 2;
870 borderWidths
[1][i
] = PRInt32(mBorderWidths
[i
]) / 2;
872 } else if (borderColorStyleCount
== 3) {
873 // with 3 color styles, any extra pixel (or lack of extra pixel)
874 // goes to the middle
875 NS_FOR_CSS_SIDES (i
) {
876 if (mBorderWidths
[i
] == 1.0) {
877 borderWidths
[0][i
] = 1.0;
878 borderWidths
[1][i
] = borderWidths
[2][i
] = 0.0;
880 PRInt32 rest
= PRInt32(mBorderWidths
[i
]) % 3;
881 borderWidths
[0][i
] = borderWidths
[2][i
] = borderWidths
[1][i
] = (PRInt32(mBorderWidths
[i
]) - rest
) / 3;
884 borderWidths
[1][i
] += 1.0;
885 } else if (rest
== 2) {
886 borderWidths
[0][i
] += 1.0;
887 borderWidths
[2][i
] += 1.0;
893 // make a copy that we can modify
894 gfxCornerSizes radii
= mBorderRadii
;
896 gfxRect
soRect(mOuterRect
);
897 gfxRect
siRect(mOuterRect
);
899 for (unsigned int i
= 0; i
< borderColorStyleCount
; i
++) {
900 // walk siRect inwards at the start of the loop to get the
901 // correct inner rect.
902 siRect
.Inset(borderWidths
[i
]);
904 if (borderColorStyle
[i
] != BorderColorStyleNone
) {
905 gfxRGBA color
= ComputeColorForLine(i
,
906 borderColorStyle
, borderColorStyleCount
,
907 borderRenderColor
, mBackgroundColor
);
909 FillSolidBorder(soRect
, siRect
, radii
, borderWidths
[i
], aSides
, color
);
912 ComputeInnerRadii(radii
, borderWidths
[i
], &radii
);
914 // And now soRect is the same as siRect, for the next line in.
920 nsCSSBorderRenderer::DrawDashedSide(PRUint8 aSide
)
925 PRUint8 style
= mBorderStyles
[aSide
];
926 gfxFloat borderWidth
= mBorderWidths
[aSide
];
927 nscolor borderColor
= mBorderColors
[aSide
];
929 if (borderWidth
== 0.0)
932 if (style
== NS_STYLE_BORDER_STYLE_NONE
||
933 style
== NS_STYLE_BORDER_STYLE_HIDDEN
)
936 if (style
== NS_STYLE_BORDER_STYLE_DASHED
) {
937 dashWidth
= gfxFloat(borderWidth
* DOT_LENGTH
* DASH_LENGTH
);
942 mContext
->SetLineCap(gfxContext::LINE_CAP_BUTT
);
943 } else if (style
== NS_STYLE_BORDER_STYLE_DOTTED
) {
944 dashWidth
= gfxFloat(borderWidth
* DOT_LENGTH
);
946 if (borderWidth
> 2.0) {
948 dash
[1] = dashWidth
* 2.0;
950 mContext
->SetLineCap(gfxContext::LINE_CAP_ROUND
);
956 SF("DrawDashedSide: style: %d!!\n", style
);
957 NS_ERROR("DrawDashedSide called with style other than DASHED or DOTTED; someone's not playing nice");
961 SF("dash: %f %f\n", dash
[0], dash
[1]);
963 mContext
->SetDash(dash
, 2, 0.0);
965 gfxPoint start
= mOuterRect
.Corner(aSide
);
966 gfxPoint end
= mOuterRect
.Corner(NEXT_SIDE(aSide
));
968 if (aSide
== NS_SIDE_TOP
) {
969 start
.x
+= mBorderCornerDimensions
[C_TL
].width
;
970 end
.x
-= mBorderCornerDimensions
[C_TR
].width
;
972 start
.y
+= borderWidth
/ 2.0;
973 end
.y
+= borderWidth
/ 2.0;
974 } else if (aSide
== NS_SIDE_RIGHT
) {
975 start
.x
-= borderWidth
/ 2.0;
976 end
.x
-= borderWidth
/ 2.0;
978 start
.y
+= mBorderCornerDimensions
[C_TR
].height
;
979 end
.y
-= mBorderCornerDimensions
[C_BR
].height
;
980 } else if (aSide
== NS_SIDE_BOTTOM
) {
981 start
.x
-= mBorderCornerDimensions
[C_BR
].width
;
982 end
.x
+= mBorderCornerDimensions
[C_BL
].width
;
984 start
.y
-= borderWidth
/ 2.0;
985 end
.y
-= borderWidth
/ 2.0;
986 } else if (aSide
== NS_SIDE_LEFT
) {
987 start
.x
+= borderWidth
/ 2.0;
988 end
.x
+= borderWidth
/ 2.0;
990 start
.y
-= mBorderCornerDimensions
[C_BL
].height
;
991 end
.y
+= mBorderCornerDimensions
[C_TL
].height
;
995 mContext
->MoveTo(start
);
996 mContext
->LineTo(end
);
997 mContext
->SetLineWidth(borderWidth
);
998 mContext
->SetColor(gfxRGBA(borderColor
));
999 //mContext->SetColor(gfxRGBA(1.0, 0.0, 0.0, 1.0));
1004 nsCSSBorderRenderer::DrawBorders()
1006 PRBool forceSeparateCorners
= PR_FALSE
;
1008 // Examine the border style to figure out if we can draw it in one
1010 PRBool tlBordersSame
= AreBorderSideFinalStylesSame(SIDE_BIT_TOP
| SIDE_BIT_LEFT
);
1011 PRBool brBordersSame
= AreBorderSideFinalStylesSame(SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
);
1012 PRBool allBordersSame
= AreBorderSideFinalStylesSame(SIDE_BITS_ALL
);
1013 if (allBordersSame
&&
1014 mCompositeColors
[0] == NULL
&&
1015 (mBorderStyles
[0] == NS_STYLE_BORDER_STYLE_NONE
||
1016 mBorderStyles
[0] == NS_STYLE_BORDER_STYLE_HIDDEN
||
1017 mBorderColors
[0] == NS_RGBA(0,0,0,0)))
1019 // All borders are the same style, and the style is either none or hidden, or the color
1021 // This doesn't check if the composite colors happen to be all transparent, but that should
1022 // happen very rarely in practice.
1026 // If we have composite colors -and- border radius,
1027 // then use separate corners so we get OPERATOR_ADD for the corners.
1028 // Otherwise, we'll get artifacts as we draw stacked 1px-wide curves.
1029 if (allBordersSame
&& mCompositeColors
[0] != nsnull
&& !mNoBorderRadius
)
1030 forceSeparateCorners
= PR_TRUE
;
1032 // round mOuterRect and mInnerRect; they're already an integer
1033 // number of pixels apart and should stay that way after
1038 S(" mOuterRect: "), S(mOuterRect
), SN();
1039 S(" mInnerRect: "), S(mInnerRect
), SN();
1040 SF(" mBorderColors: 0x%08x 0x%08x 0x%08x 0x%08x\n", mBorderColors
[0], mBorderColors
[1], mBorderColors
[2], mBorderColors
[3]);
1042 // if conditioning the outside rect failed, then bail -- the outside
1043 // rect is supposed to enclose the entire border
1044 mOuterRect
.Condition();
1045 if (mOuterRect
.IsEmpty())
1048 mInnerRect
.Condition();
1050 PRIntn dashedSides
= 0;
1051 NS_FOR_CSS_SIDES(i
) {
1052 PRUint8 style
= mBorderStyles
[i
];
1053 if (style
== NS_STYLE_BORDER_STYLE_DASHED
||
1054 style
== NS_STYLE_BORDER_STYLE_DOTTED
)
1056 // pretend that all borders aren't the same; we need to draw
1057 // things separately for dashed/dotting
1058 allBordersSame
= PR_FALSE
;
1059 dashedSides
|= (1 << i
);
1062 // just bail out entirely if RULES_MARKER
1063 // got through (see bug 379419).
1064 if (style
& NS_STYLE_BORDER_STYLE_RULES_MARKER
)
1068 SF(" allBordersSame: %d dashedSides: 0x%02x\n", allBordersSame
, dashedSides
);
1070 // Clamp the CTM to be pixel-aligned; we do this only
1071 // for translation-only matrices now, but we could do it
1072 // if the matrix has just a scale as well. We should not
1073 // do it if there's a rotation.
1074 gfxMatrix mat
= mContext
->CurrentMatrix();
1075 if (!mat
.HasNonTranslation()) {
1076 mat
.x0
= floor(mat
.x0
+ 0.5);
1077 mat
.y0
= floor(mat
.y0
+ 0.5);
1078 mContext
->SetMatrix(mat
);
1081 if (allBordersSame
&& !forceSeparateCorners
) {
1082 /* Draw everything in one go */
1083 DrawBorderSides(SIDE_BITS_ALL
);
1084 SN("---------------- (1)");
1086 /* We have more than one pass to go. Draw the corners separately from the sides. */
1089 * If we have a 1px-wide border, the corners are going to be
1090 * negligible, so don't bother doing anything fancy. Just extend
1091 * the top and bottom borders to the right 1px and the left border
1092 * to the bottom 1px. We do this by twiddling the corner dimensions,
1093 * which causes the right to happen later on. Only do this if we have
1094 * a 1.0 unit border all around and no border radius.
1097 for (int corner
= 0; corner
< gfxCorner::NUM_CORNERS
; corner
++) {
1098 const PRIntn sides
[2] = { corner
, PREV_SIDE(corner
) };
1100 if (!IsZeroSize(mBorderRadii
[corner
]))
1103 if (mBorderWidths
[sides
[0]] == 1.0 && mBorderWidths
[sides
[1]] == 1.0) {
1104 if (corner
== gfxCorner::TOP_LEFT
|| corner
== gfxCorner::TOP_RIGHT
)
1105 mBorderCornerDimensions
[corner
].width
= 0.0;
1107 mBorderCornerDimensions
[corner
].height
= 0.0;
1111 // First, the corners
1112 for (int corner
= 0; corner
< gfxCorner::NUM_CORNERS
; corner
++) {
1113 // if there's no corner, don't do all this work for it
1114 if (IsZeroSize(mBorderCornerDimensions
[corner
]))
1117 const PRIntn sides
[2] = { corner
, PREV_SIDE(corner
) };
1118 PRIntn sideBits
= (1 << sides
[0]) | (1 << sides
[1]);
1120 PRBool simpleCornerStyle
= mCompositeColors
[sides
[0]] == NULL
&&
1121 mCompositeColors
[sides
[1]] == NULL
&&
1122 AreBorderSideFinalStylesSame(sideBits
);
1124 // If we don't have anything complex going on in this corner,
1125 // then we can just fill the corner with a solid color, and avoid
1126 // the potentially expensive clip.
1127 if (simpleCornerStyle
&&
1128 IsZeroSize(mBorderRadii
[corner
]) &&
1129 IsSolidCornerStyle(mBorderStyles
[sides
[0]], corner
))
1131 mContext
->NewPath();
1132 DoCornerSubPath(corner
);
1133 mContext
->SetColor(MakeBorderColor(mBorderColors
[sides
[0]],
1135 BorderColorStyleForSolidCorner(mBorderStyles
[sides
[0]], corner
)));
1142 // clip to the corner
1143 mContext
->NewPath();
1144 DoCornerSubPath(corner
);
1147 if (simpleCornerStyle
) {
1148 // we don't need a group for this corner, the sides are the same,
1149 // but we weren't able to render just a solid block for the corner.
1150 DrawBorderSides(sideBits
);
1152 // Sides are different. We need to draw using OPERATOR_ADD to
1153 // get correct color blending behaviour at the seam. We need
1154 // to do it in an offscreen surface to ensure that we're
1155 // always compositing on transparent black. If the colors
1156 // don't have transparency and the current destination surface
1157 // has an alpha channel, we could just clear the region and
1158 // avoid the temporary, but that situation doesn't happen all
1159 // that often in practice (we double buffer to no-alpha
1162 mContext
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
1163 mContext
->SetOperator(gfxContext::OPERATOR_ADD
);
1165 for (int cornerSide
= 0; cornerSide
< 2; cornerSide
++) {
1166 PRUint8 side
= sides
[cornerSide
];
1167 PRUint8 style
= mBorderStyles
[side
];
1169 SF("corner: %d cornerSide: %d side: %d style: %d\n", corner
, cornerSide
, side
, style
);
1173 mContext
->NewPath();
1174 DoSideClipSubPath(side
);
1177 DrawBorderSides(1 << side
);
1179 mContext
->Restore();
1182 mContext
->PopGroupToSource();
1183 mContext
->SetOperator(gfxContext::OPERATOR_OVER
);
1187 mContext
->Restore();
1192 // in the case of a single-unit border, we already munged the
1193 // corners up above; so we can just draw the top left and bottom
1194 // right sides separately, if they're the same.
1196 // We need to check for mNoBorderRadius, because when there is
1197 // one, FillSolidBorder always draws the full rounded rectangle
1198 // and expects there to be a clip in place.
1199 PRIntn alreadyDrawnSides
= 0;
1200 if (mOneUnitBorder
&&
1202 (dashedSides
& (SIDE_BIT_TOP
| SIDE_BIT_LEFT
)) == 0)
1204 if (tlBordersSame
) {
1205 DrawBorderSides(SIDE_BIT_TOP
| SIDE_BIT_LEFT
);
1206 alreadyDrawnSides
|= (SIDE_BIT_TOP
| SIDE_BIT_LEFT
);
1209 if (brBordersSame
&& (dashedSides
& (SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
)) == 0) {
1210 DrawBorderSides(SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
);
1211 alreadyDrawnSides
|= (SIDE_BIT_BOTTOM
| SIDE_BIT_RIGHT
);
1215 // We're done with the corners, now draw the sides.
1216 NS_FOR_CSS_SIDES (side
) {
1217 // if we drew it above, skip it
1218 if (alreadyDrawnSides
& (1 << side
))
1221 // If there's no border on this side, skip it
1222 if (mBorderWidths
[side
] == 0.0 ||
1223 mBorderStyles
[side
] == NS_STYLE_BORDER_STYLE_HIDDEN
||
1224 mBorderStyles
[side
] == NS_STYLE_BORDER_STYLE_NONE
)
1228 if (dashedSides
& (1 << side
)) {
1229 // Dashed sides will always draw just the part ignoring the
1230 // corners for the side, so no need to clip.
1231 DrawDashedSide (side
);
1233 SN("---------------- (d)");
1237 // Undashed sides will currently draw the entire side,
1238 // including parts that would normally be covered by a corner,
1239 // so we need to clip.
1241 // XXX Optimization -- it would be good to make this work like
1242 // DrawDashedSide, and have a DrawOneSide function that just
1243 // draws one side and not the corners, because then we can
1244 // avoid the potentially expensive clip.
1246 mContext
->NewPath();
1247 DoSideClipWithoutCornersSubPath(side
);
1250 DrawBorderSides(1 << side
);
1252 mContext
->Restore();
1254 SN("---------------- (*)");