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
5 : *
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/
10 : *
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
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Corporation
20 : * Portions created by the Initial Developer are Copyright (C) 2008
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Vladimir Vukicevic <vladimir@pobox.com>
25 : * Bas Schouten <bschouten@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsStyleConsts.h"
42 : #include "nsIFrame.h"
43 : #include "nsPoint.h"
44 : #include "nsRect.h"
45 : #include "nsIViewManager.h"
46 : #include "nsFrameManager.h"
47 : #include "nsStyleContext.h"
48 : #include "nsGkAtoms.h"
49 : #include "nsCSSAnonBoxes.h"
50 : #include "nsTransform2D.h"
51 : #include "nsIContent.h"
52 : #include "nsIDocument.h"
53 : #include "nsIScrollableFrame.h"
54 : #include "imgIRequest.h"
55 : #include "imgIContainer.h"
56 : #include "nsCSSRendering.h"
57 : #include "nsCSSColorUtils.h"
58 : #include "nsITheme.h"
59 : #include "nsThemeConstants.h"
60 : #include "nsIServiceManager.h"
61 : #include "nsIHTMLDocument.h"
62 : #include "nsLayoutUtils.h"
63 : #include "nsINameSpaceManager.h"
64 : #include "nsBlockFrame.h"
65 :
66 : #include "gfxContext.h"
67 :
68 : #include "nsCSSRenderingBorders.h"
69 :
70 : /**
71 : * nsCSSRendering::PaintBorder
72 : * nsCSSRendering::PaintOutline
73 : * -> DrawBorders
74 : *
75 : * DrawBorders
76 : * -> Ability to use specialized approach?
77 : * |- Draw using specialized function
78 : * |- separate corners?
79 : * |- dashed side mask
80 : * |
81 : * -> can border be drawn in 1 pass? (e.g., solid border same color all around)
82 : * |- DrawBorderSides with all 4 sides
83 : * -> more than 1 pass?
84 : * |- for each corner
85 : * |- clip to DoCornerClipSubPath
86 : * |- PushGroup
87 : * |- for each side adjacent to corner
88 : * |- clip to DoSideClipSubPath
89 : * |- DrawBorderSides with one side
90 : * |- PopGroup
91 : * |- for each side
92 : * |- DoSideClipWithoutCornersSubPath
93 : * |- DrawDashedSide || DrawBorderSides with one side
94 : */
95 :
96 : static void ComputeBorderCornerDimensions(const gfxRect& aOuterRect,
97 : const gfxRect& aInnerRect,
98 : const gfxCornerSizes& aRadii,
99 : gfxCornerSizes *aDimsResult);
100 :
101 : // given a side index, get the previous and next side index
102 : #define NEXT_SIDE(_s) mozilla::css::Side(((_s) + 1) & 3)
103 : #define PREV_SIDE(_s) mozilla::css::Side(((_s) + 3) & 3)
104 :
105 : // from the given base color and the background color, turn
106 : // color into a color for the given border pattern style
107 : static gfxRGBA MakeBorderColor(const gfxRGBA& aColor,
108 : const gfxRGBA& aBackgroundColor,
109 : BorderColorStyle aBorderColorStyle);
110 :
111 :
112 : // Given a line index (an index starting from the outside of the
113 : // border going inwards) and an array of line styles, calculate the
114 : // color that that stripe of the border should be rendered in.
115 : static gfxRGBA ComputeColorForLine(PRUint32 aLineIndex,
116 : const BorderColorStyle* aBorderColorStyle,
117 : PRUint32 aBorderColorStyleCount,
118 : nscolor aBorderColor,
119 : nscolor aBackgroundColor);
120 :
121 : static gfxRGBA ComputeCompositeColorForLine(PRUint32 aLineIndex,
122 : const nsBorderColors* aBorderColors);
123 :
124 : // little helper function to check if the array of 4 floats given are
125 : // equal to the given value
126 : static bool
127 0 : CheckFourFloatsEqual(const gfxFloat *vals, gfxFloat k)
128 : {
129 0 : return (vals[0] == k &&
130 0 : vals[1] == k &&
131 0 : vals[2] == k &&
132 0 : vals[3] == k);
133 : }
134 :
135 : static bool
136 0 : IsZeroSize(const gfxSize& sz) {
137 0 : return sz.width == 0.0 || sz.height == 0.0;
138 : }
139 :
140 : static bool
141 0 : AllCornersZeroSize(const gfxCornerSizes& corners) {
142 0 : return IsZeroSize(corners[NS_CORNER_TOP_LEFT]) &&
143 0 : IsZeroSize(corners[NS_CORNER_TOP_RIGHT]) &&
144 0 : IsZeroSize(corners[NS_CORNER_BOTTOM_RIGHT]) &&
145 0 : IsZeroSize(corners[NS_CORNER_BOTTOM_LEFT]);
146 : }
147 :
148 : typedef enum {
149 : // Normal solid square corner. Will be rectangular, the size of the
150 : // adjacent sides. If the corner has a border radius, the corner
151 : // will always be solid, since we don't do dotted/dashed etc.
152 : CORNER_NORMAL,
153 :
154 : // Paint the corner in whatever style is not dotted/dashed of the
155 : // adjacent corners.
156 : CORNER_SOLID,
157 :
158 : // Paint the corner as a dot, the size of the bigger of the adjacent
159 : // sides.
160 : CORNER_DOT
161 : } CornerStyle;
162 :
163 0 : nsCSSBorderRenderer::nsCSSBorderRenderer(PRInt32 aAppUnitsPerPixel,
164 : gfxContext* aDestContext,
165 : gfxRect& aOuterRect,
166 : const PRUint8* aBorderStyles,
167 : const gfxFloat* aBorderWidths,
168 : gfxCornerSizes& aBorderRadii,
169 : const nscolor* aBorderColors,
170 : nsBorderColors* const* aCompositeColors,
171 : PRIntn aSkipSides,
172 : nscolor aBackgroundColor)
173 : : mContext(aDestContext),
174 : mOuterRect(aOuterRect),
175 : mBorderStyles(aBorderStyles),
176 : mBorderWidths(aBorderWidths),
177 : mBorderRadii(aBorderRadii),
178 : mBorderColors(aBorderColors),
179 : mCompositeColors(aCompositeColors),
180 : mAUPP(aAppUnitsPerPixel),
181 : mSkipSides(aSkipSides),
182 0 : mBackgroundColor(aBackgroundColor)
183 : {
184 0 : if (!mCompositeColors) {
185 : static nsBorderColors * const noColors[4] = { NULL };
186 0 : mCompositeColors = &noColors[0];
187 : }
188 :
189 0 : mInnerRect = mOuterRect;
190 : mInnerRect.Deflate(
191 0 : gfxMargin(mBorderStyles[3] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[3] : 0,
192 0 : mBorderStyles[0] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[0] : 0,
193 0 : mBorderStyles[1] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[1] : 0,
194 0 : mBorderStyles[2] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[2] : 0));
195 :
196 0 : ComputeBorderCornerDimensions(mOuterRect, mInnerRect, mBorderRadii, &mBorderCornerDimensions);
197 :
198 0 : mOneUnitBorder = CheckFourFloatsEqual(mBorderWidths, 1.0);
199 0 : mNoBorderRadius = AllCornersZeroSize(mBorderRadii);
200 0 : mAvoidStroke = false;
201 0 : }
202 :
203 : /* static */ void
204 0 : nsCSSBorderRenderer::ComputeInnerRadii(const gfxCornerSizes& aRadii,
205 : const gfxFloat *aBorderSizes,
206 : gfxCornerSizes *aInnerRadiiRet)
207 : {
208 0 : gfxCornerSizes& iRadii = *aInnerRadiiRet;
209 :
210 0 : iRadii[C_TL].width = NS_MAX(0.0, aRadii[C_TL].width - aBorderSizes[NS_SIDE_LEFT]);
211 0 : iRadii[C_TL].height = NS_MAX(0.0, aRadii[C_TL].height - aBorderSizes[NS_SIDE_TOP]);
212 :
213 0 : iRadii[C_TR].width = NS_MAX(0.0, aRadii[C_TR].width - aBorderSizes[NS_SIDE_RIGHT]);
214 0 : iRadii[C_TR].height = NS_MAX(0.0, aRadii[C_TR].height - aBorderSizes[NS_SIDE_TOP]);
215 :
216 0 : iRadii[C_BR].width = NS_MAX(0.0, aRadii[C_BR].width - aBorderSizes[NS_SIDE_RIGHT]);
217 0 : iRadii[C_BR].height = NS_MAX(0.0, aRadii[C_BR].height - aBorderSizes[NS_SIDE_BOTTOM]);
218 :
219 0 : iRadii[C_BL].width = NS_MAX(0.0, aRadii[C_BL].width - aBorderSizes[NS_SIDE_LEFT]);
220 0 : iRadii[C_BL].height = NS_MAX(0.0, aRadii[C_BL].height - aBorderSizes[NS_SIDE_BOTTOM]);
221 0 : }
222 :
223 : /*static*/ void
224 0 : ComputeBorderCornerDimensions(const gfxRect& aOuterRect,
225 : const gfxRect& aInnerRect,
226 : const gfxCornerSizes& aRadii,
227 : gfxCornerSizes *aDimsRet)
228 : {
229 0 : gfxFloat leftWidth = aInnerRect.X() - aOuterRect.X();
230 0 : gfxFloat topWidth = aInnerRect.Y() - aOuterRect.Y();
231 0 : gfxFloat rightWidth = aOuterRect.Width() - aInnerRect.Width() - leftWidth;
232 0 : gfxFloat bottomWidth = aOuterRect.Height() - aInnerRect.Height() - topWidth;
233 :
234 0 : if (AllCornersZeroSize(aRadii)) {
235 : // These will always be in pixel units from CSS
236 0 : (*aDimsRet)[C_TL] = gfxSize(leftWidth, topWidth);
237 0 : (*aDimsRet)[C_TR] = gfxSize(rightWidth, topWidth);
238 0 : (*aDimsRet)[C_BR] = gfxSize(rightWidth, bottomWidth);
239 0 : (*aDimsRet)[C_BL] = gfxSize(leftWidth, bottomWidth);
240 : } else {
241 : // Always round up to whole pixels for the corners; it's safe to
242 : // make the corners bigger than necessary, and this way we ensure
243 : // that we avoid seams.
244 0 : (*aDimsRet)[C_TL] = gfxSize(ceil(NS_MAX(leftWidth, aRadii[C_TL].width)),
245 0 : ceil(NS_MAX(topWidth, aRadii[C_TL].height)));
246 0 : (*aDimsRet)[C_TR] = gfxSize(ceil(NS_MAX(rightWidth, aRadii[C_TR].width)),
247 0 : ceil(NS_MAX(topWidth, aRadii[C_TR].height)));
248 0 : (*aDimsRet)[C_BR] = gfxSize(ceil(NS_MAX(rightWidth, aRadii[C_BR].width)),
249 0 : ceil(NS_MAX(bottomWidth, aRadii[C_BR].height)));
250 0 : (*aDimsRet)[C_BL] = gfxSize(ceil(NS_MAX(leftWidth, aRadii[C_BL].width)),
251 0 : ceil(NS_MAX(bottomWidth, aRadii[C_BL].height)));
252 : }
253 0 : }
254 :
255 : bool
256 0 : nsCSSBorderRenderer::AreBorderSideFinalStylesSame(PRUint8 aSides)
257 : {
258 0 : NS_ASSERTION(aSides != 0 && (aSides & ~SIDE_BITS_ALL) == 0,
259 : "AreBorderSidesSame: invalid whichSides!");
260 :
261 : /* First check if the specified styles and colors are the same for all sides */
262 0 : int firstStyle = 0;
263 0 : NS_FOR_CSS_SIDES (i) {
264 0 : if (firstStyle == i) {
265 0 : if (((1 << i) & aSides) == 0)
266 0 : firstStyle++;
267 0 : continue;
268 : }
269 :
270 0 : if (((1 << i) & aSides) == 0) {
271 0 : continue;
272 : }
273 :
274 0 : if (mBorderStyles[firstStyle] != mBorderStyles[i] ||
275 0 : mBorderColors[firstStyle] != mBorderColors[i] ||
276 0 : !nsBorderColors::Equal(mCompositeColors[firstStyle],
277 0 : mCompositeColors[i]))
278 0 : return false;
279 : }
280 :
281 : /* Then if it's one of the two-tone styles and we're not
282 : * just comparing the TL or BR sides */
283 0 : switch (mBorderStyles[firstStyle]) {
284 : case NS_STYLE_BORDER_STYLE_GROOVE:
285 : case NS_STYLE_BORDER_STYLE_RIDGE:
286 : case NS_STYLE_BORDER_STYLE_INSET:
287 : case NS_STYLE_BORDER_STYLE_OUTSET:
288 : return ((aSides & ~(SIDE_BIT_TOP | SIDE_BIT_LEFT)) == 0 ||
289 0 : (aSides & ~(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) == 0);
290 : }
291 :
292 0 : return true;
293 : }
294 :
295 : bool
296 0 : nsCSSBorderRenderer::IsSolidCornerStyle(PRUint8 aStyle, mozilla::css::Corner aCorner)
297 : {
298 0 : switch (aStyle) {
299 : case NS_STYLE_BORDER_STYLE_DOTTED:
300 : case NS_STYLE_BORDER_STYLE_DASHED:
301 : case NS_STYLE_BORDER_STYLE_SOLID:
302 0 : return true;
303 :
304 : case NS_STYLE_BORDER_STYLE_INSET:
305 : case NS_STYLE_BORDER_STYLE_OUTSET:
306 0 : return (aCorner == NS_CORNER_TOP_LEFT || aCorner == NS_CORNER_BOTTOM_RIGHT);
307 :
308 : case NS_STYLE_BORDER_STYLE_GROOVE:
309 : case NS_STYLE_BORDER_STYLE_RIDGE:
310 0 : return mOneUnitBorder && (aCorner == NS_CORNER_TOP_LEFT || aCorner == NS_CORNER_BOTTOM_RIGHT);
311 :
312 : case NS_STYLE_BORDER_STYLE_DOUBLE:
313 0 : return mOneUnitBorder;
314 :
315 : default:
316 0 : return false;
317 : }
318 : }
319 :
320 : BorderColorStyle
321 0 : nsCSSBorderRenderer::BorderColorStyleForSolidCorner(PRUint8 aStyle, mozilla::css::Corner aCorner)
322 : {
323 : // note that this function assumes that the corner is already solid,
324 : // as per the earlier function
325 0 : switch (aStyle) {
326 : case NS_STYLE_BORDER_STYLE_DOTTED:
327 : case NS_STYLE_BORDER_STYLE_DASHED:
328 : case NS_STYLE_BORDER_STYLE_SOLID:
329 : case NS_STYLE_BORDER_STYLE_DOUBLE:
330 0 : return BorderColorStyleSolid;
331 :
332 : case NS_STYLE_BORDER_STYLE_INSET:
333 : case NS_STYLE_BORDER_STYLE_GROOVE:
334 0 : if (aCorner == NS_CORNER_TOP_LEFT)
335 0 : return BorderColorStyleDark;
336 0 : else if (aCorner == NS_CORNER_BOTTOM_RIGHT)
337 0 : return BorderColorStyleLight;
338 0 : break;
339 :
340 : case NS_STYLE_BORDER_STYLE_OUTSET:
341 : case NS_STYLE_BORDER_STYLE_RIDGE:
342 0 : if (aCorner == NS_CORNER_TOP_LEFT)
343 0 : return BorderColorStyleLight;
344 0 : else if (aCorner == NS_CORNER_BOTTOM_RIGHT)
345 0 : return BorderColorStyleDark;
346 0 : break;
347 : }
348 :
349 0 : return BorderColorStyleNone;
350 : }
351 :
352 : void
353 0 : nsCSSBorderRenderer::DoCornerSubPath(mozilla::css::Corner aCorner)
354 : {
355 0 : gfxPoint offset(0.0, 0.0);
356 :
357 0 : if (aCorner == C_TR || aCorner == C_BR)
358 0 : offset.x = mOuterRect.Width() - mBorderCornerDimensions[aCorner].width;
359 0 : if (aCorner == C_BR || aCorner == C_BL)
360 0 : offset.y = mOuterRect.Height() - mBorderCornerDimensions[aCorner].height;
361 :
362 0 : mContext->Rectangle(gfxRect(mOuterRect.TopLeft() + offset,
363 0 : mBorderCornerDimensions[aCorner]));
364 0 : }
365 :
366 : void
367 0 : nsCSSBorderRenderer::DoSideClipWithoutCornersSubPath(mozilla::css::Side aSide)
368 : {
369 0 : gfxPoint offset(0.0, 0.0);
370 :
371 : // The offset from the outside rect to the start of this side's
372 : // box. For the top and bottom sides, the height of the box
373 : // must be the border height; the x start must take into account
374 : // the corner size (which may be bigger than the right or left
375 : // side's width). The same applies to the right and left sides.
376 0 : if (aSide == NS_SIDE_TOP) {
377 0 : offset.x = mBorderCornerDimensions[C_TL].width;
378 0 : } else if (aSide == NS_SIDE_RIGHT) {
379 0 : offset.x = mOuterRect.Width() - mBorderWidths[NS_SIDE_RIGHT];
380 0 : offset.y = mBorderCornerDimensions[C_TR].height;
381 0 : } else if (aSide == NS_SIDE_BOTTOM) {
382 0 : offset.x = mBorderCornerDimensions[C_BL].width;
383 0 : offset.y = mOuterRect.Height() - mBorderWidths[NS_SIDE_BOTTOM];
384 0 : } else if (aSide == NS_SIDE_LEFT) {
385 0 : offset.y = mBorderCornerDimensions[C_TL].height;
386 : }
387 :
388 : // The sum of the width & height of the corners adjacent to the
389 : // side. This relies on the relationship between side indexing and
390 : // corner indexing; that is, 0 == SIDE_TOP and 0 == CORNER_TOP_LEFT,
391 : // with both proceeding clockwise.
392 0 : gfxSize sideCornerSum = mBorderCornerDimensions[mozilla::css::Corner(aSide)]
393 0 : + mBorderCornerDimensions[mozilla::css::Corner(NEXT_SIDE(aSide))];
394 0 : gfxRect rect(mOuterRect.TopLeft() + offset,
395 0 : mOuterRect.Size() - sideCornerSum);
396 :
397 0 : if (aSide == NS_SIDE_TOP || aSide == NS_SIDE_BOTTOM)
398 0 : rect.height = mBorderWidths[aSide];
399 : else
400 0 : rect.width = mBorderWidths[aSide];
401 :
402 0 : mContext->Rectangle(rect);
403 0 : }
404 :
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.
408 :
409 : typedef enum {
410 : // clip to the trapezoid formed by the corners of the
411 : // inner and outer rectangles for the given side
412 : SIDE_CLIP_TRAPEZOID,
413 :
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
418 : // to the middle.
419 : //
420 : // This is needed for correctly clipping rounded borders,
421 : // which might extend past the SIDE_CLIP_TRAPEZOID trap.
422 : SIDE_CLIP_TRAPEZOID_FULL,
423 :
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.
427 : SIDE_CLIP_RECTANGLE
428 : } SideClipType;
429 :
430 : // Given three points, p0, p1, and midPoint, move p1 further in to the
431 : // rectangle (of which aMidPoint is the center) so that it reaches the
432 : // closer of the horizontal or vertical lines intersecting the midpoint,
433 : // while maintaing the slope of the line. If p0 and p1 are the same,
434 : // just move p1 to midPoint (since there's no slope to maintain).
435 : // FIXME: Extending only to the midpoint isn't actually sufficient for
436 : // boxes with asymmetric radii.
437 : static void
438 0 : MaybeMoveToMidPoint(gfxPoint& aP0, gfxPoint& aP1, const gfxPoint& aMidPoint)
439 : {
440 0 : gfxPoint ps = aP1 - aP0;
441 :
442 0 : if (ps.x == 0.0) {
443 0 : if (ps.y == 0.0) {
444 0 : aP1 = aMidPoint;
445 : } else {
446 0 : aP1.y = aMidPoint.y;
447 : }
448 : } else {
449 0 : if (ps.y == 0.0) {
450 0 : aP1.x = aMidPoint.x;
451 : } else {
452 : gfxFloat k = NS_MIN((aMidPoint.x - aP0.x) / ps.x,
453 0 : (aMidPoint.y - aP0.y) / ps.y);
454 0 : aP1 = aP0 + ps * k;
455 : }
456 : }
457 0 : }
458 :
459 : void
460 0 : nsCSSBorderRenderer::DoSideClipSubPath(mozilla::css::Side aSide)
461 : {
462 : // the clip proceeds clockwise from the top left corner;
463 : // so "start" in each case is the start of the region from that side.
464 : //
465 : // the final path will be formed like:
466 : // s0 ------- e0
467 : // | /
468 : // s1 ----- e1
469 : //
470 : // that is, the second point will always be on the inside
471 :
472 0 : gfxPoint start[2];
473 0 : gfxPoint end[2];
474 :
475 : #define IS_DASHED_OR_DOTTED(_s) ((_s) == NS_STYLE_BORDER_STYLE_DASHED || (_s) == NS_STYLE_BORDER_STYLE_DOTTED)
476 0 : bool isDashed = IS_DASHED_OR_DOTTED(mBorderStyles[aSide]);
477 0 : bool startIsDashed = IS_DASHED_OR_DOTTED(mBorderStyles[PREV_SIDE(aSide)]);
478 0 : bool endIsDashed = IS_DASHED_OR_DOTTED(mBorderStyles[NEXT_SIDE(aSide)]);
479 : #undef IS_DASHED_OR_DOTTED
480 :
481 0 : SideClipType startType = SIDE_CLIP_TRAPEZOID;
482 0 : SideClipType endType = SIDE_CLIP_TRAPEZOID;
483 :
484 0 : if (!IsZeroSize(mBorderRadii[mozilla::css::Corner(aSide)]))
485 0 : startType = SIDE_CLIP_TRAPEZOID_FULL;
486 0 : else if (startIsDashed && isDashed)
487 0 : startType = SIDE_CLIP_RECTANGLE;
488 :
489 0 : if (!IsZeroSize(mBorderRadii[mozilla::css::Corner(NEXT_SIDE(aSide))]))
490 0 : endType = SIDE_CLIP_TRAPEZOID_FULL;
491 0 : else if (endIsDashed && isDashed)
492 0 : endType = SIDE_CLIP_RECTANGLE;
493 :
494 0 : gfxPoint midPoint = mInnerRect.Center();
495 :
496 0 : start[0] = mOuterRect.CCWCorner(aSide);
497 0 : start[1] = mInnerRect.CCWCorner(aSide);
498 :
499 0 : end[0] = mOuterRect.CWCorner(aSide);
500 0 : end[1] = mInnerRect.CWCorner(aSide);
501 :
502 0 : if (startType == SIDE_CLIP_TRAPEZOID_FULL) {
503 0 : MaybeMoveToMidPoint(start[0], start[1], midPoint);
504 0 : } else if (startType == SIDE_CLIP_RECTANGLE) {
505 0 : if (aSide == NS_SIDE_TOP || aSide == NS_SIDE_BOTTOM)
506 0 : start[1] = gfxPoint(mOuterRect.CCWCorner(aSide).x, mInnerRect.CCWCorner(aSide).y);
507 : else
508 0 : start[1] = gfxPoint(mInnerRect.CCWCorner(aSide).x, mOuterRect.CCWCorner(aSide).y);
509 : }
510 :
511 0 : if (endType == SIDE_CLIP_TRAPEZOID_FULL) {
512 0 : MaybeMoveToMidPoint(end[0], end[1], midPoint);
513 0 : } else if (endType == SIDE_CLIP_RECTANGLE) {
514 0 : if (aSide == NS_SIDE_TOP || aSide == NS_SIDE_BOTTOM)
515 0 : end[0] = gfxPoint(mInnerRect.CWCorner(aSide).x, mOuterRect.CWCorner(aSide).y);
516 : else
517 0 : end[0] = gfxPoint(mOuterRect.CWCorner(aSide).x, mInnerRect.CWCorner(aSide).y);
518 : }
519 :
520 0 : mContext->MoveTo(start[0]);
521 0 : mContext->LineTo(end[0]);
522 0 : mContext->LineTo(end[1]);
523 0 : mContext->LineTo(start[1]);
524 0 : mContext->ClosePath();
525 0 : }
526 :
527 : void
528 0 : nsCSSBorderRenderer::FillSolidBorder(const gfxRect& aOuterRect,
529 : const gfxRect& aInnerRect,
530 : const gfxCornerSizes& aBorderRadii,
531 : const gfxFloat *aBorderSizes,
532 : PRIntn aSides,
533 : const gfxRGBA& aColor)
534 : {
535 0 : mContext->SetColor(aColor);
536 : // Note that this function is allowed to draw more than just the
537 : // requested sides.
538 :
539 : // If we have a border radius, do full rounded rectangles
540 : // and fill, regardless of what sides we're asked to draw.
541 0 : if (!AllCornersZeroSize(aBorderRadii)) {
542 0 : gfxCornerSizes innerRadii;
543 0 : ComputeInnerRadii(aBorderRadii, aBorderSizes, &innerRadii);
544 :
545 0 : mContext->NewPath();
546 :
547 : // do the outer border
548 0 : mContext->RoundedRectangle(aOuterRect, aBorderRadii, true);
549 :
550 : // then do the inner border CCW
551 0 : mContext->RoundedRectangle(aInnerRect, innerRadii, false);
552 :
553 0 : mContext->Fill();
554 :
555 0 : return;
556 : }
557 :
558 : // If we're asked to draw all sides of an equal-sized border,
559 : // stroking is fastest. This is a fairly common path, but partial
560 : // sides is probably second in the list -- there are a bunch of
561 : // common border styles, such as inset and outset, that are
562 : // top-left/bottom-right split.
563 0 : if (aSides == SIDE_BITS_ALL &&
564 0 : CheckFourFloatsEqual(aBorderSizes, aBorderSizes[0]) &&
565 0 : !mAvoidStroke)
566 : {
567 0 : gfxRect r(aOuterRect);
568 0 : r.Deflate(aBorderSizes[0] / 2.0);
569 0 : mContext->SetLineWidth(aBorderSizes[0]);
570 :
571 0 : mContext->NewPath();
572 0 : mContext->Rectangle(r);
573 0 : mContext->Stroke();
574 :
575 0 : return;
576 : }
577 :
578 : // Otherwise, we have unequal sized borders or we're only
579 : // drawing some sides; create rectangles for each side
580 : // and fill them.
581 :
582 0 : gfxRect r[4];
583 :
584 : // compute base rects for each side
585 0 : if (aSides & SIDE_BIT_TOP) {
586 : r[NS_SIDE_TOP] =
587 : gfxRect(aOuterRect.X(), aOuterRect.Y(),
588 0 : aOuterRect.Width(), aBorderSizes[NS_SIDE_TOP]);
589 : }
590 :
591 0 : if (aSides & SIDE_BIT_BOTTOM) {
592 : r[NS_SIDE_BOTTOM] =
593 0 : gfxRect(aOuterRect.X(), aOuterRect.YMost() - aBorderSizes[NS_SIDE_BOTTOM],
594 0 : aOuterRect.Width(), aBorderSizes[NS_SIDE_BOTTOM]);
595 : }
596 :
597 0 : if (aSides & SIDE_BIT_LEFT) {
598 : r[NS_SIDE_LEFT] =
599 : gfxRect(aOuterRect.X(), aOuterRect.Y(),
600 0 : aBorderSizes[NS_SIDE_LEFT], aOuterRect.Height());
601 : }
602 :
603 0 : if (aSides & SIDE_BIT_RIGHT) {
604 : r[NS_SIDE_RIGHT] =
605 0 : gfxRect(aOuterRect.XMost() - aBorderSizes[NS_SIDE_RIGHT], aOuterRect.Y(),
606 0 : aBorderSizes[NS_SIDE_RIGHT], aOuterRect.Height());
607 : }
608 :
609 : // If two sides meet at a corner that we're rendering, then
610 : // make sure that we adjust one of the sides to avoid overlap.
611 : // This is especially important in the case of colors with
612 : // an alpha channel.
613 :
614 0 : if ((aSides & (SIDE_BIT_TOP | SIDE_BIT_LEFT)) == (SIDE_BIT_TOP | SIDE_BIT_LEFT)) {
615 : // adjust the left's top down a bit
616 0 : r[NS_SIDE_LEFT].y += aBorderSizes[NS_SIDE_TOP];
617 0 : r[NS_SIDE_LEFT].height -= aBorderSizes[NS_SIDE_TOP];
618 : }
619 :
620 0 : if ((aSides & (SIDE_BIT_TOP | SIDE_BIT_RIGHT)) == (SIDE_BIT_TOP | SIDE_BIT_RIGHT)) {
621 : // adjust the top's left a bit
622 0 : r[NS_SIDE_TOP].width -= aBorderSizes[NS_SIDE_RIGHT];
623 : }
624 :
625 0 : if ((aSides & (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) == (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) {
626 : // adjust the right's bottom a bit
627 0 : r[NS_SIDE_RIGHT].height -= aBorderSizes[NS_SIDE_BOTTOM];
628 : }
629 :
630 0 : if ((aSides & (SIDE_BIT_BOTTOM | SIDE_BIT_LEFT)) == (SIDE_BIT_BOTTOM | SIDE_BIT_LEFT)) {
631 : // adjust the bottom's left a bit
632 0 : r[NS_SIDE_BOTTOM].x += aBorderSizes[NS_SIDE_LEFT];
633 0 : r[NS_SIDE_BOTTOM].width -= aBorderSizes[NS_SIDE_LEFT];
634 : }
635 :
636 : // Filling these one by one is faster than filling them all at once.
637 0 : for (PRUint32 i = 0; i < 4; i++) {
638 0 : if (aSides & (1 << i)) {
639 0 : mContext->NewPath();
640 0 : mContext->Rectangle(r[i], true);
641 0 : mContext->Fill();
642 : }
643 : }
644 : }
645 :
646 : gfxRGBA
647 0 : MakeBorderColor(const gfxRGBA& aColor, const gfxRGBA& aBackgroundColor, BorderColorStyle aBorderColorStyle)
648 : {
649 : nscolor colors[2];
650 0 : int k = 0;
651 :
652 0 : switch (aBorderColorStyle) {
653 : case BorderColorStyleNone:
654 0 : return gfxRGBA(0.0, 0.0, 0.0, 0.0);
655 :
656 : case BorderColorStyleLight:
657 0 : k = 1;
658 : /* fall through */
659 : case BorderColorStyleDark:
660 0 : NS_GetSpecial3DColors(colors, aBackgroundColor.Packed(), aColor.Packed());
661 0 : return gfxRGBA(colors[k]);
662 :
663 : case BorderColorStyleSolid:
664 : default:
665 0 : return aColor;
666 : }
667 : }
668 :
669 : gfxRGBA
670 0 : ComputeColorForLine(PRUint32 aLineIndex,
671 : const BorderColorStyle* aBorderColorStyle,
672 : PRUint32 aBorderColorStyleCount,
673 : nscolor aBorderColor,
674 : nscolor aBackgroundColor)
675 : {
676 0 : NS_ASSERTION(aLineIndex < aBorderColorStyleCount, "Invalid lineIndex given");
677 :
678 0 : return MakeBorderColor(gfxRGBA(aBorderColor), gfxRGBA(aBackgroundColor), aBorderColorStyle[aLineIndex]);
679 : }
680 :
681 : gfxRGBA
682 0 : ComputeCompositeColorForLine(PRUint32 aLineIndex,
683 : const nsBorderColors* aBorderColors)
684 : {
685 0 : while (aLineIndex-- && aBorderColors->mNext)
686 0 : aBorderColors = aBorderColors->mNext;
687 :
688 0 : return gfxRGBA(aBorderColors->mColor);
689 : }
690 :
691 : void
692 0 : nsCSSBorderRenderer::DrawBorderSidesCompositeColors(PRIntn aSides, const nsBorderColors *aCompositeColors)
693 : {
694 0 : gfxCornerSizes radii = mBorderRadii;
695 :
696 : // the generic composite colors path; each border is 1px in size
697 0 : gfxRect soRect = mOuterRect;
698 0 : gfxFloat maxBorderWidth = 0;
699 0 : NS_FOR_CSS_SIDES (i) {
700 0 : maxBorderWidth = NS_MAX(maxBorderWidth, mBorderWidths[i]);
701 : }
702 :
703 : gfxFloat fakeBorderSizes[4];
704 :
705 0 : gfxPoint itl = mInnerRect.TopLeft();
706 0 : gfxPoint ibr = mInnerRect.BottomRight();
707 :
708 0 : for (PRUint32 i = 0; i < PRUint32(maxBorderWidth); i++) {
709 0 : gfxRGBA lineColor = ComputeCompositeColorForLine(i, aCompositeColors);
710 :
711 0 : gfxRect siRect = soRect;
712 0 : siRect.Deflate(1.0);
713 :
714 : // now cap the rects to the real mInnerRect
715 0 : gfxPoint tl = siRect.TopLeft();
716 0 : gfxPoint br = siRect.BottomRight();
717 :
718 0 : tl.x = NS_MIN(tl.x, itl.x);
719 0 : tl.y = NS_MIN(tl.y, itl.y);
720 :
721 0 : br.x = NS_MAX(br.x, ibr.x);
722 0 : br.y = NS_MAX(br.y, ibr.y);
723 :
724 0 : siRect = gfxRect(tl.x, tl.y, br.x - tl.x , br.y - tl.y);
725 :
726 0 : fakeBorderSizes[NS_SIDE_TOP] = siRect.TopLeft().y - soRect.TopLeft().y;
727 0 : fakeBorderSizes[NS_SIDE_RIGHT] = soRect.TopRight().x - siRect.TopRight().x;
728 0 : fakeBorderSizes[NS_SIDE_BOTTOM] = soRect.BottomRight().y - siRect.BottomRight().y;
729 0 : fakeBorderSizes[NS_SIDE_LEFT] = siRect.BottomLeft().x - soRect.BottomLeft().x;
730 :
731 0 : FillSolidBorder(soRect, siRect, radii, fakeBorderSizes, aSides, lineColor);
732 :
733 0 : soRect = siRect;
734 :
735 0 : ComputeInnerRadii(radii, fakeBorderSizes, &radii);
736 : }
737 0 : }
738 :
739 : void
740 0 : nsCSSBorderRenderer::DrawBorderSides(PRIntn aSides)
741 : {
742 0 : if (aSides == 0 || (aSides & ~SIDE_BITS_ALL) != 0) {
743 0 : NS_WARNING("DrawBorderSides: invalid sides!");
744 0 : return;
745 : }
746 :
747 : PRUint8 borderRenderStyle;
748 : nscolor borderRenderColor;
749 0 : const nsBorderColors *compositeColors = nsnull;
750 :
751 0 : PRUint32 borderColorStyleCount = 0;
752 : BorderColorStyle borderColorStyleTopLeft[3], borderColorStyleBottomRight[3];
753 0 : BorderColorStyle *borderColorStyle = nsnull;
754 :
755 0 : NS_FOR_CSS_SIDES (i) {
756 0 : if ((aSides & (1 << i)) == 0)
757 0 : continue;
758 0 : borderRenderStyle = mBorderStyles[i];
759 0 : borderRenderColor = mBorderColors[i];
760 0 : compositeColors = mCompositeColors[i];
761 0 : break;
762 : }
763 :
764 0 : if (borderRenderStyle == NS_STYLE_BORDER_STYLE_NONE ||
765 : borderRenderStyle == NS_STYLE_BORDER_STYLE_HIDDEN)
766 0 : return;
767 :
768 : // -moz-border-colors is a hack; if we have it for a border, then
769 : // it's always drawn solid, and each color is given 1px. The last
770 : // color is used for the remainder of the border's size. Just
771 : // hand off to another function to do all that.
772 0 : if (compositeColors) {
773 0 : DrawBorderSidesCompositeColors(aSides, compositeColors);
774 0 : return;
775 : }
776 :
777 : // We're not doing compositeColors, so we can calculate the
778 : // borderColorStyle based on the specified style. The
779 : // borderColorStyle array goes from the outer to the inner style.
780 : //
781 : // If the border width is 1, we need to change the borderRenderStyle
782 : // a bit to make sure that we get the right colors -- e.g. 'ridge'
783 : // with a 1px border needs to look like solid, not like 'outset'.
784 0 : if (mOneUnitBorder &&
785 : (borderRenderStyle == NS_STYLE_BORDER_STYLE_RIDGE ||
786 : borderRenderStyle == NS_STYLE_BORDER_STYLE_GROOVE ||
787 : borderRenderStyle == NS_STYLE_BORDER_STYLE_DOUBLE))
788 0 : borderRenderStyle = NS_STYLE_BORDER_STYLE_SOLID;
789 :
790 0 : switch (borderRenderStyle) {
791 : case NS_STYLE_BORDER_STYLE_SOLID:
792 : case NS_STYLE_BORDER_STYLE_DASHED:
793 : case NS_STYLE_BORDER_STYLE_DOTTED:
794 0 : borderColorStyleTopLeft[0] = BorderColorStyleSolid;
795 :
796 0 : borderColorStyleBottomRight[0] = BorderColorStyleSolid;
797 :
798 0 : borderColorStyleCount = 1;
799 0 : break;
800 :
801 : case NS_STYLE_BORDER_STYLE_GROOVE:
802 0 : borderColorStyleTopLeft[0] = BorderColorStyleDark;
803 0 : borderColorStyleTopLeft[1] = BorderColorStyleLight;
804 :
805 0 : borderColorStyleBottomRight[0] = BorderColorStyleLight;
806 0 : borderColorStyleBottomRight[1] = BorderColorStyleDark;
807 :
808 0 : borderColorStyleCount = 2;
809 0 : break;
810 :
811 : case NS_STYLE_BORDER_STYLE_RIDGE:
812 0 : borderColorStyleTopLeft[0] = BorderColorStyleLight;
813 0 : borderColorStyleTopLeft[1] = BorderColorStyleDark;
814 :
815 0 : borderColorStyleBottomRight[0] = BorderColorStyleDark;
816 0 : borderColorStyleBottomRight[1] = BorderColorStyleLight;
817 :
818 0 : borderColorStyleCount = 2;
819 0 : break;
820 :
821 : case NS_STYLE_BORDER_STYLE_DOUBLE:
822 0 : borderColorStyleTopLeft[0] = BorderColorStyleSolid;
823 0 : borderColorStyleTopLeft[1] = BorderColorStyleNone;
824 0 : borderColorStyleTopLeft[2] = BorderColorStyleSolid;
825 :
826 0 : borderColorStyleBottomRight[0] = BorderColorStyleSolid;
827 0 : borderColorStyleBottomRight[1] = BorderColorStyleNone;
828 0 : borderColorStyleBottomRight[2] = BorderColorStyleSolid;
829 :
830 0 : borderColorStyleCount = 3;
831 0 : break;
832 :
833 : case NS_STYLE_BORDER_STYLE_INSET:
834 0 : borderColorStyleTopLeft[0] = BorderColorStyleDark;
835 0 : borderColorStyleBottomRight[0] = BorderColorStyleLight;
836 :
837 0 : borderColorStyleCount = 1;
838 0 : break;
839 :
840 : case NS_STYLE_BORDER_STYLE_OUTSET:
841 0 : borderColorStyleTopLeft[0] = BorderColorStyleLight;
842 0 : borderColorStyleBottomRight[0] = BorderColorStyleDark;
843 :
844 0 : borderColorStyleCount = 1;
845 0 : break;
846 :
847 : default:
848 0 : NS_NOTREACHED("Unhandled border style!!");
849 0 : break;
850 : }
851 :
852 : // The only way to get to here is by having a
853 : // borderColorStyleCount < 1 or > 3; this should never happen,
854 : // since -moz-border-colors doesn't get handled here.
855 0 : NS_ASSERTION(borderColorStyleCount > 0 && borderColorStyleCount < 4,
856 : "Non-border-colors case with borderColorStyleCount < 1 or > 3; what happened?");
857 :
858 : // The caller should never give us anything with a mix
859 : // of TL/BR if the border style would require a
860 : // TL/BR split.
861 0 : if (aSides & (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT))
862 0 : borderColorStyle = borderColorStyleBottomRight;
863 : else
864 0 : borderColorStyle = borderColorStyleTopLeft;
865 :
866 : // Distribute the border across the available space.
867 : gfxFloat borderWidths[3][4];
868 :
869 0 : if (borderColorStyleCount == 1) {
870 0 : NS_FOR_CSS_SIDES (i) {
871 0 : borderWidths[0][i] = mBorderWidths[i];
872 : }
873 0 : } else if (borderColorStyleCount == 2) {
874 : // with 2 color styles, any extra pixel goes to the outside
875 0 : NS_FOR_CSS_SIDES (i) {
876 0 : borderWidths[0][i] = PRInt32(mBorderWidths[i]) / 2 + PRInt32(mBorderWidths[i]) % 2;
877 0 : borderWidths[1][i] = PRInt32(mBorderWidths[i]) / 2;
878 : }
879 0 : } else if (borderColorStyleCount == 3) {
880 : // with 3 color styles, any extra pixel (or lack of extra pixel)
881 : // goes to the middle
882 0 : NS_FOR_CSS_SIDES (i) {
883 0 : if (mBorderWidths[i] == 1.0) {
884 0 : borderWidths[0][i] = 1.0;
885 0 : borderWidths[1][i] = borderWidths[2][i] = 0.0;
886 : } else {
887 0 : PRInt32 rest = PRInt32(mBorderWidths[i]) % 3;
888 0 : borderWidths[0][i] = borderWidths[2][i] = borderWidths[1][i] = (PRInt32(mBorderWidths[i]) - rest) / 3;
889 :
890 0 : if (rest == 1) {
891 0 : borderWidths[1][i] += 1.0;
892 0 : } else if (rest == 2) {
893 0 : borderWidths[0][i] += 1.0;
894 0 : borderWidths[2][i] += 1.0;
895 : }
896 : }
897 : }
898 : }
899 :
900 : // make a copy that we can modify
901 0 : gfxCornerSizes radii = mBorderRadii;
902 :
903 0 : gfxRect soRect(mOuterRect);
904 0 : gfxRect siRect(mOuterRect);
905 :
906 0 : for (unsigned int i = 0; i < borderColorStyleCount; i++) {
907 : // walk siRect inwards at the start of the loop to get the
908 : // correct inner rect.
909 : siRect.Deflate(gfxMargin(borderWidths[i][3], borderWidths[i][0],
910 0 : borderWidths[i][1], borderWidths[i][2]));
911 :
912 0 : if (borderColorStyle[i] != BorderColorStyleNone) {
913 : gfxRGBA color = ComputeColorForLine(i,
914 : borderColorStyle, borderColorStyleCount,
915 0 : borderRenderColor, mBackgroundColor);
916 :
917 0 : FillSolidBorder(soRect, siRect, radii, borderWidths[i], aSides, color);
918 : }
919 :
920 0 : ComputeInnerRadii(radii, borderWidths[i], &radii);
921 :
922 : // And now soRect is the same as siRect, for the next line in.
923 0 : soRect = siRect;
924 : }
925 : }
926 :
927 : void
928 0 : nsCSSBorderRenderer::DrawDashedSide(mozilla::css::Side aSide)
929 : {
930 : gfxFloat dashWidth;
931 : gfxFloat dash[2];
932 :
933 0 : PRUint8 style = mBorderStyles[aSide];
934 0 : gfxFloat borderWidth = mBorderWidths[aSide];
935 0 : nscolor borderColor = mBorderColors[aSide];
936 :
937 0 : if (borderWidth == 0.0)
938 0 : return;
939 :
940 0 : if (style == NS_STYLE_BORDER_STYLE_NONE ||
941 : style == NS_STYLE_BORDER_STYLE_HIDDEN)
942 0 : return;
943 :
944 0 : if (style == NS_STYLE_BORDER_STYLE_DASHED) {
945 0 : dashWidth = gfxFloat(borderWidth * DOT_LENGTH * DASH_LENGTH);
946 :
947 0 : dash[0] = dashWidth;
948 0 : dash[1] = dashWidth;
949 :
950 0 : mContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
951 0 : } else if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
952 0 : dashWidth = gfxFloat(borderWidth * DOT_LENGTH);
953 :
954 0 : if (borderWidth > 2.0) {
955 0 : dash[0] = 0.0;
956 0 : dash[1] = dashWidth * 2.0;
957 :
958 0 : mContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
959 : } else {
960 0 : dash[0] = dashWidth;
961 0 : dash[1] = dashWidth;
962 : }
963 : } else {
964 0 : SF("DrawDashedSide: style: %d!!\n", style);
965 0 : NS_ERROR("DrawDashedSide called with style other than DASHED or DOTTED; someone's not playing nice");
966 0 : return;
967 : }
968 :
969 0 : SF("dash: %f %f\n", dash[0], dash[1]);
970 :
971 0 : mContext->SetDash(dash, 2, 0.0);
972 :
973 0 : gfxPoint start = mOuterRect.CCWCorner(aSide);
974 0 : gfxPoint end = mOuterRect.CWCorner(aSide);
975 :
976 0 : if (aSide == NS_SIDE_TOP) {
977 0 : start.x += mBorderCornerDimensions[C_TL].width;
978 0 : end.x -= mBorderCornerDimensions[C_TR].width;
979 :
980 0 : start.y += borderWidth / 2.0;
981 0 : end.y += borderWidth / 2.0;
982 0 : } else if (aSide == NS_SIDE_RIGHT) {
983 0 : start.x -= borderWidth / 2.0;
984 0 : end.x -= borderWidth / 2.0;
985 :
986 0 : start.y += mBorderCornerDimensions[C_TR].height;
987 0 : end.y -= mBorderCornerDimensions[C_BR].height;
988 0 : } else if (aSide == NS_SIDE_BOTTOM) {
989 0 : start.x -= mBorderCornerDimensions[C_BR].width;
990 0 : end.x += mBorderCornerDimensions[C_BL].width;
991 :
992 0 : start.y -= borderWidth / 2.0;
993 0 : end.y -= borderWidth / 2.0;
994 0 : } else if (aSide == NS_SIDE_LEFT) {
995 0 : start.x += borderWidth / 2.0;
996 0 : end.x += borderWidth / 2.0;
997 :
998 0 : start.y -= mBorderCornerDimensions[C_BL].height;
999 0 : end.y += mBorderCornerDimensions[C_TL].height;
1000 : }
1001 :
1002 0 : mContext->NewPath();
1003 0 : mContext->MoveTo(start);
1004 0 : mContext->LineTo(end);
1005 0 : mContext->SetLineWidth(borderWidth);
1006 0 : mContext->SetColor(gfxRGBA(borderColor));
1007 : //mContext->SetColor(gfxRGBA(1.0, 0.0, 0.0, 1.0));
1008 0 : mContext->Stroke();
1009 : }
1010 :
1011 : void
1012 0 : nsCSSBorderRenderer::SetupStrokeStyle(mozilla::css::Side aSide)
1013 : {
1014 0 : mContext->SetColor(gfxRGBA(mBorderColors[aSide]));
1015 0 : mContext->SetLineWidth(mBorderWidths[aSide]);
1016 0 : }
1017 :
1018 : bool
1019 0 : nsCSSBorderRenderer::AllBordersSameWidth()
1020 : {
1021 0 : if (mBorderWidths[0] == mBorderWidths[1] &&
1022 0 : mBorderWidths[0] == mBorderWidths[2] &&
1023 0 : mBorderWidths[0] == mBorderWidths[3])
1024 : {
1025 0 : return true;
1026 : }
1027 :
1028 0 : return false;
1029 : }
1030 :
1031 : bool
1032 0 : nsCSSBorderRenderer::AllBordersSolid(bool *aHasCompositeColors)
1033 : {
1034 0 : *aHasCompositeColors = false;
1035 0 : NS_FOR_CSS_SIDES(i) {
1036 0 : if (mCompositeColors[i] != nsnull) {
1037 0 : *aHasCompositeColors = true;
1038 : }
1039 0 : if (mBorderStyles[i] == NS_STYLE_BORDER_STYLE_SOLID ||
1040 0 : mBorderStyles[i] == NS_STYLE_BORDER_STYLE_NONE ||
1041 0 : mBorderStyles[i] == NS_STYLE_BORDER_STYLE_HIDDEN)
1042 : {
1043 0 : continue;
1044 : }
1045 0 : return false;
1046 : }
1047 :
1048 0 : return true;
1049 : }
1050 :
1051 0 : bool IsVisible(int aStyle)
1052 : {
1053 0 : if (aStyle != NS_STYLE_BORDER_STYLE_NONE &&
1054 : aStyle != NS_STYLE_BORDER_STYLE_HIDDEN) {
1055 0 : return true;
1056 : }
1057 0 : return false;
1058 : }
1059 :
1060 : already_AddRefed<gfxPattern>
1061 0 : nsCSSBorderRenderer::CreateCornerGradient(mozilla::css::Corner aCorner,
1062 : const gfxRGBA &aFirstColor,
1063 : const gfxRGBA &aSecondColor)
1064 : {
1065 : typedef struct { gfxFloat a, b; } twoFloats;
1066 :
1067 : const twoFloats gradientCoeff[4] = { { -1, +1 },
1068 : { -1, -1 },
1069 : { +1, -1 },
1070 0 : { +1, +1 } };
1071 :
1072 : // Sides which form the 'width' and 'height' for the calculation of the angle
1073 : // for our gradient.
1074 0 : const int cornerWidth[4] = { 3, 1, 1, 3 };
1075 0 : const int cornerHeight[4] = { 0, 0, 2, 2 };
1076 :
1077 0 : gfxPoint cornerOrigin = mOuterRect.AtCorner(aCorner);
1078 :
1079 0 : gfxPoint pat1, pat2;
1080 : pat1.x = cornerOrigin.x +
1081 0 : mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a;
1082 : pat1.y = cornerOrigin.y +
1083 0 : mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b;
1084 : pat2.x = cornerOrigin.x -
1085 0 : mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a;
1086 : pat2.y = cornerOrigin.y -
1087 0 : mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b;
1088 :
1089 : float gradientOffset;
1090 :
1091 0 : if (mContext->IsCairo() &&
1092 0 : (mContext->OriginalSurface()->GetType() == gfxASurface::SurfaceTypeD2D ||
1093 0 : mContext->OriginalSurface()->GetType() == gfxASurface::SurfaceTypeQuartz))
1094 : {
1095 : // On quarz this doesn't do exactly the right thing, but it does do what
1096 : // most other browsers do and doing the 'right' thing seems to be
1097 : // hard with the quartz cairo backend.
1098 0 : gradientOffset = 0;
1099 : } else {
1100 : // When cairo/Azure does the gradient drawing this gives us pretty nice behavior!
1101 0 : gradientOffset = 0.25 / sqrt(pow(mBorderWidths[cornerHeight[aCorner]], 2) +
1102 0 : pow(mBorderWidths[cornerHeight[aCorner]], 2));
1103 : }
1104 :
1105 0 : nsRefPtr<gfxPattern> pattern = new gfxPattern(pat1.x, pat1.y, pat2.x, pat2.y);
1106 0 : pattern->AddColorStop(0.5 - gradientOffset, gfxRGBA(aFirstColor));
1107 0 : pattern->AddColorStop(0.5 + gradientOffset, gfxRGBA(aSecondColor));
1108 :
1109 0 : return pattern.forget();
1110 : }
1111 :
1112 : typedef struct { gfxFloat a, b; } twoFloats;
1113 :
1114 : void
1115 0 : nsCSSBorderRenderer::DrawSingleWidthSolidBorder()
1116 : {
1117 : // Easy enough to deal with.
1118 0 : mContext->SetLineWidth(1);
1119 0 : gfxRect rect = mOuterRect;
1120 0 : rect.Deflate(0.5);
1121 :
1122 : const twoFloats cornerAdjusts[4] = { { +0.5, 0 },
1123 : { 0, +0.5 },
1124 : { -0.5, 0 },
1125 0 : { 0, -0.5 } };
1126 :
1127 :
1128 0 : NS_FOR_CSS_SIDES(side) {
1129 0 : gfxPoint firstCorner = rect.CCWCorner(side);
1130 0 : firstCorner.x += cornerAdjusts[side].a;
1131 0 : firstCorner.y += cornerAdjusts[side].b;
1132 0 : gfxPoint secondCorner = rect.CWCorner(side);
1133 0 : secondCorner.x += cornerAdjusts[side].a;
1134 0 : secondCorner.y += cornerAdjusts[side].b;
1135 :
1136 0 : mContext->SetColor(gfxRGBA(mBorderColors[side]));
1137 0 : mContext->NewPath();
1138 0 : mContext->MoveTo(firstCorner);
1139 0 : mContext->LineTo(secondCorner);
1140 0 : mContext->Stroke();
1141 : }
1142 0 : }
1143 :
1144 : void
1145 0 : nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder()
1146 : {
1147 0 : const gfxFloat alpha = 0.55191497064665766025;
1148 :
1149 : const twoFloats cornerMults[4] = { { -1, 0 },
1150 : { 0, -1 },
1151 : { +1, 0 },
1152 0 : { 0, +1 } };
1153 :
1154 : const twoFloats centerAdjusts[4] = { { 0, +0.5 },
1155 : { -0.5, 0 },
1156 : { 0, -0.5 },
1157 0 : { +0.5, 0 } };
1158 :
1159 0 : gfxPoint pc, pci, p0, p1, p2, p3, pd, p3i;
1160 :
1161 0 : gfxCornerSizes innerRadii;
1162 0 : ComputeInnerRadii(mBorderRadii, mBorderWidths, &innerRadii);
1163 :
1164 0 : gfxRect strokeRect = mOuterRect;
1165 0 : strokeRect.Deflate(gfxMargin(mBorderWidths[3] / 2.0, mBorderWidths[0] / 2.0,
1166 0 : mBorderWidths[1] / 2.0, mBorderWidths[2] / 2.0));
1167 :
1168 0 : NS_FOR_CSS_CORNERS(i) {
1169 : // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
1170 0 : mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4);
1171 0 : mozilla::css::Corner prevCorner = mozilla::css::Corner(i);
1172 :
1173 : // i+2 and i+3 respectively. These are used to index into the corner
1174 : // multiplier table, and were deduced by calculating out the long form
1175 : // of each corner and finding a pattern in the signs and values.
1176 0 : int i1 = (i+1) % 4;
1177 0 : int i2 = (i+2) % 4;
1178 0 : int i3 = (i+3) % 4;
1179 :
1180 0 : pc = mOuterRect.AtCorner(c);
1181 0 : pci = mInnerRect.AtCorner(c);
1182 0 : mContext->SetLineWidth(mBorderWidths[i]);
1183 :
1184 : nscolor firstColor, secondColor;
1185 0 : if (IsVisible(mBorderStyles[i]) && IsVisible(mBorderStyles[i1])) {
1186 0 : firstColor = mBorderColors[i];
1187 0 : secondColor = mBorderColors[i1];
1188 0 : } else if (IsVisible(mBorderStyles[i])) {
1189 0 : firstColor = mBorderColors[i];
1190 0 : secondColor = mBorderColors[i];
1191 : } else {
1192 0 : firstColor = mBorderColors[i1];
1193 0 : secondColor = mBorderColors[i1];
1194 : }
1195 :
1196 0 : mContext->NewPath();
1197 :
1198 0 : gfxPoint strokeStart, strokeEnd;
1199 :
1200 0 : strokeStart.x = mOuterRect.AtCorner(prevCorner).x +
1201 0 : mBorderCornerDimensions[prevCorner].width * cornerMults[i2].a;
1202 0 : strokeStart.y = mOuterRect.AtCorner(prevCorner).y +
1203 0 : mBorderCornerDimensions[prevCorner].height * cornerMults[i2].b;
1204 :
1205 0 : strokeEnd.x = pc.x + mBorderCornerDimensions[c].width * cornerMults[i].a;
1206 0 : strokeEnd.y = pc.y + mBorderCornerDimensions[c].height * cornerMults[i].b;
1207 :
1208 0 : strokeStart.x += centerAdjusts[i].a * mBorderWidths[i];
1209 0 : strokeStart.y += centerAdjusts[i].b * mBorderWidths[i];
1210 0 : strokeEnd.x += centerAdjusts[i].a * mBorderWidths[i];
1211 0 : strokeEnd.y += centerAdjusts[i].b * mBorderWidths[i];
1212 :
1213 0 : mContext->MoveTo(strokeStart);
1214 0 : mContext->LineTo(strokeEnd);
1215 0 : mContext->SetColor(gfxRGBA(mBorderColors[i]));
1216 0 : mContext->Stroke();
1217 :
1218 0 : if (firstColor != secondColor) {
1219 : nsRefPtr<gfxPattern> pattern =
1220 0 : CreateCornerGradient(c, firstColor, secondColor);
1221 0 : mContext->SetPattern(pattern);
1222 : } else {
1223 0 : mContext->SetColor(firstColor);
1224 : }
1225 :
1226 0 : if (mBorderRadii[c].width > 0 && mBorderRadii[c].height > 0) {
1227 0 : p0.x = pc.x + cornerMults[i].a * mBorderRadii[c].width;
1228 0 : p0.y = pc.y + cornerMults[i].b * mBorderRadii[c].height;
1229 :
1230 0 : p3.x = pc.x + cornerMults[i3].a * mBorderRadii[c].width;
1231 0 : p3.y = pc.y + cornerMults[i3].b * mBorderRadii[c].height;
1232 :
1233 0 : p1.x = p0.x + alpha * cornerMults[i2].a * mBorderRadii[c].width;
1234 0 : p1.y = p0.y + alpha * cornerMults[i2].b * mBorderRadii[c].height;
1235 :
1236 0 : p2.x = p3.x - alpha * cornerMults[i3].a * mBorderRadii[c].width;
1237 0 : p2.y = p3.y - alpha * cornerMults[i3].b * mBorderRadii[c].height;
1238 :
1239 0 : mContext->NewPath();
1240 :
1241 0 : gfxPoint cornerStart;
1242 0 : cornerStart.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width;
1243 0 : cornerStart.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height;
1244 :
1245 0 : mContext->MoveTo(cornerStart);
1246 0 : mContext->LineTo(p0);
1247 :
1248 0 : mContext->CurveTo(p1, p2, p3);
1249 :
1250 0 : gfxPoint outerCornerEnd;
1251 0 : outerCornerEnd.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width;
1252 0 : outerCornerEnd.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height;
1253 :
1254 0 : mContext->LineTo(outerCornerEnd);
1255 :
1256 0 : p0.x = pci.x + cornerMults[i].a * innerRadii[c].width;
1257 0 : p0.y = pci.y + cornerMults[i].b * innerRadii[c].height;
1258 :
1259 0 : p3i.x = pci.x + cornerMults[i3].a * innerRadii[c].width;
1260 0 : p3i.y = pci.y + cornerMults[i3].b * innerRadii[c].height;
1261 :
1262 0 : p1.x = p0.x + alpha * cornerMults[i2].a * innerRadii[c].width;
1263 0 : p1.y = p0.y + alpha * cornerMults[i2].b * innerRadii[c].height;
1264 :
1265 0 : p2.x = p3i.x - alpha * cornerMults[i3].a * innerRadii[c].width;
1266 0 : p2.y = p3i.y - alpha * cornerMults[i3].b * innerRadii[c].height;
1267 0 : mContext->LineTo(p3i);
1268 0 : mContext->CurveTo(p2, p1, p0);
1269 0 : mContext->ClosePath();
1270 0 : mContext->Fill();
1271 : } else {
1272 0 : gfxPoint c1, c2, c3, c4;
1273 :
1274 0 : c1.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width;
1275 0 : c1.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height;
1276 0 : c2 = pc;
1277 0 : c3.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width;
1278 0 : c3.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height;
1279 :
1280 0 : mContext->NewPath();
1281 0 : mContext->MoveTo(c1);
1282 0 : mContext->LineTo(c2);
1283 0 : mContext->LineTo(c3);
1284 0 : mContext->LineTo(pci);
1285 0 : mContext->ClosePath();
1286 :
1287 0 : mContext->Fill();
1288 : }
1289 : }
1290 0 : }
1291 :
1292 : void
1293 0 : nsCSSBorderRenderer::DrawRectangularCompositeColors()
1294 : {
1295 : nsBorderColors *currentColors[4];
1296 0 : mContext->SetLineWidth(1);
1297 0 : memcpy(currentColors, mCompositeColors, sizeof(nsBorderColors*) * 4);
1298 0 : gfxRect rect = mOuterRect;
1299 0 : rect.Deflate(0.5);
1300 :
1301 : const twoFloats cornerAdjusts[4] = { { +0.5, 0 },
1302 : { 0, +0.5 },
1303 : { -0.5, 0 },
1304 0 : { 0, -0.5 } };
1305 :
1306 0 : for (int i = 0; i < mBorderWidths[0]; i++) {
1307 0 : NS_FOR_CSS_SIDES(side) {
1308 0 : int sideNext = (side + 1) % 4;
1309 :
1310 0 : gfxPoint firstCorner = rect.CCWCorner(side);
1311 0 : firstCorner.x += cornerAdjusts[side].a;
1312 0 : firstCorner.y += cornerAdjusts[side].b;
1313 0 : gfxPoint secondCorner = rect.CWCorner(side);
1314 0 : secondCorner.x -= cornerAdjusts[side].a;
1315 0 : secondCorner.y -= cornerAdjusts[side].b;
1316 :
1317 : gfxRGBA currentColor =
1318 0 : currentColors[side] ? gfxRGBA(currentColors[side]->mColor)
1319 0 : : gfxRGBA(mBorderColors[side]);
1320 :
1321 0 : mContext->SetColor(currentColor);
1322 0 : mContext->NewPath();
1323 0 : mContext->MoveTo(firstCorner);
1324 0 : mContext->LineTo(secondCorner);
1325 0 : mContext->Stroke();
1326 :
1327 0 : mContext->NewPath();
1328 0 : gfxPoint cornerTopLeft = rect.CWCorner(side);
1329 0 : cornerTopLeft.x -= 0.5;
1330 0 : cornerTopLeft.y -= 0.5;
1331 0 : mContext->Rectangle(gfxRect(cornerTopLeft, gfxSize(1, 1)));
1332 : gfxRGBA nextColor =
1333 0 : currentColors[sideNext] ? gfxRGBA(currentColors[sideNext]->mColor)
1334 0 : : gfxRGBA(mBorderColors[sideNext]);
1335 :
1336 : gfxRGBA cornerColor((currentColor.r + nextColor.r) / 2.0,
1337 : (currentColor.g + nextColor.g) / 2.0,
1338 : (currentColor.b + nextColor.b) / 2.0,
1339 0 : (currentColor.a + nextColor.a) / 2.0);
1340 0 : mContext->SetColor(cornerColor);
1341 0 : mContext->Fill();
1342 :
1343 0 : if (side != 0) {
1344 : // We'll have to keep side 0 for the color averaging on side 3.
1345 0 : if (currentColors[side] && currentColors[side]->mNext) {
1346 0 : currentColors[side] = currentColors[side]->mNext;
1347 : }
1348 : }
1349 : }
1350 : // Now advance the color for side 0.
1351 0 : if (currentColors[0] && currentColors[0]->mNext) {
1352 0 : currentColors[0] = currentColors[0]->mNext;
1353 : }
1354 0 : rect.Deflate(1);
1355 : }
1356 0 : }
1357 :
1358 : void
1359 0 : nsCSSBorderRenderer::DrawBorders()
1360 : {
1361 0 : bool forceSeparateCorners = false;
1362 :
1363 : // Examine the border style to figure out if we can draw it in one
1364 : // go or not.
1365 0 : bool tlBordersSame = AreBorderSideFinalStylesSame(SIDE_BIT_TOP | SIDE_BIT_LEFT);
1366 0 : bool brBordersSame = AreBorderSideFinalStylesSame(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT);
1367 0 : bool allBordersSame = AreBorderSideFinalStylesSame(SIDE_BITS_ALL);
1368 0 : if (allBordersSame &&
1369 0 : ((mCompositeColors[0] == NULL &&
1370 0 : (mBorderStyles[0] == NS_STYLE_BORDER_STYLE_NONE ||
1371 0 : mBorderStyles[0] == NS_STYLE_BORDER_STYLE_HIDDEN ||
1372 0 : mBorderColors[0] == NS_RGBA(0,0,0,0))) ||
1373 0 : (mCompositeColors[0] &&
1374 0 : (mCompositeColors[0]->mColor == NS_RGBA(0,0,0,0) &&
1375 0 : !mCompositeColors[0]->mNext))))
1376 : {
1377 : // All borders are the same style, and the style is either none or hidden, or the color
1378 : // is transparent.
1379 : // This also checks if the first composite color is transparent, and there are
1380 : // no others. It doesn't check if there are subsequent transparent ones, because
1381 : // that would be very silly.
1382 0 : return;
1383 : }
1384 :
1385 0 : gfxMatrix mat = mContext->CurrentMatrix();
1386 :
1387 : // Clamp the CTM to be pixel-aligned; we do this only
1388 : // for translation-only matrices now, but we could do it
1389 : // if the matrix has just a scale as well. We should not
1390 : // do it if there's a rotation.
1391 0 : if (mat.HasNonTranslation()) {
1392 0 : if (!mat.HasNonAxisAlignedTransform()) {
1393 : // Scale + transform. Avoid stroke fast-paths so that we have a chance
1394 : // of snapping to pixel boundaries.
1395 0 : mAvoidStroke = true;
1396 : }
1397 : } else {
1398 0 : mat.x0 = floor(mat.x0 + 0.5);
1399 0 : mat.y0 = floor(mat.y0 + 0.5);
1400 0 : mContext->SetMatrix(mat);
1401 :
1402 : // round mOuterRect and mInnerRect; they're already an integer
1403 : // number of pixels apart and should stay that way after
1404 : // rounding. We don't do this if there's a scale in the current transform
1405 : // since this loses information that might be relevant when we're scaling.
1406 0 : mOuterRect.Round();
1407 0 : mInnerRect.Round();
1408 : }
1409 :
1410 0 : bool allBordersSameWidth = AllBordersSameWidth();
1411 :
1412 0 : if (allBordersSameWidth && mBorderWidths[0] == 0.0) {
1413 : // Some of the allBordersSameWidth codepaths depend on the border
1414 : // width being greater than zero.
1415 0 : return;
1416 : }
1417 :
1418 : bool allBordersSolid;
1419 0 : bool noCornerOutsideCenter = true;
1420 :
1421 : // First there's a couple of 'special cases' that have specifically optimized
1422 : // drawing paths, when none of these can be used we move on to the generalized
1423 : // border drawing code.
1424 0 : if (allBordersSame &&
1425 0 : mCompositeColors[0] == NULL &&
1426 : allBordersSameWidth &&
1427 0 : mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
1428 : mNoBorderRadius &&
1429 0 : !mAvoidStroke)
1430 : {
1431 : // Very simple case.
1432 0 : SetupStrokeStyle(NS_SIDE_TOP);
1433 0 : gfxRect rect = mOuterRect;
1434 0 : rect.Deflate(mBorderWidths[0] / 2.0);
1435 0 : mContext->NewPath();
1436 0 : mContext->Rectangle(rect);
1437 0 : mContext->Stroke();
1438 0 : return;
1439 : }
1440 :
1441 0 : if (allBordersSame &&
1442 0 : mCompositeColors[0] == NULL &&
1443 : allBordersSameWidth &&
1444 0 : mBorderStyles[0] == NS_STYLE_BORDER_STYLE_DOTTED &&
1445 0 : mBorderWidths[0] < 3 &&
1446 : mNoBorderRadius &&
1447 0 : !mAvoidStroke)
1448 : {
1449 : // Very simple case. We draw this rectangular dotted borner without
1450 : // antialiasing. The dots should be pixel aligned.
1451 0 : SetupStrokeStyle(NS_SIDE_TOP);
1452 :
1453 0 : gfxFloat dash = mBorderWidths[0];
1454 0 : mContext->SetDash(&dash, 1, 0.5);
1455 0 : mContext->SetAntialiasMode(gfxContext::MODE_ALIASED);
1456 0 : gfxRect rect = mOuterRect;
1457 0 : rect.Deflate(mBorderWidths[0] / 2.0);
1458 0 : mContext->NewPath();
1459 0 : mContext->Rectangle(rect);
1460 0 : mContext->Stroke();
1461 0 : return;
1462 : }
1463 :
1464 :
1465 0 : if (allBordersSame &&
1466 : allBordersSameWidth &&
1467 0 : mCompositeColors[0] == NULL &&
1468 0 : mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
1469 0 : !mAvoidStroke)
1470 : {
1471 0 : NS_FOR_CSS_CORNERS(i) {
1472 0 : if (mBorderRadii[i].width <= mBorderWidths[0]) {
1473 0 : noCornerOutsideCenter = false;
1474 : }
1475 0 : if (mBorderRadii[i].height <= mBorderWidths[0]) {
1476 0 : noCornerOutsideCenter = false;
1477 : }
1478 : }
1479 :
1480 : // We can only do a stroke here if all border radii centers are inside the
1481 : // inner rect, otherwise we get rendering artifacts.
1482 :
1483 0 : if (noCornerOutsideCenter) {
1484 : // Relatively simple case.
1485 0 : SetupStrokeStyle(NS_SIDE_TOP);
1486 0 : mOuterRect.Deflate(mBorderWidths[0] / 2.0);
1487 0 : NS_FOR_CSS_CORNERS(corner) {
1488 0 : if (mBorderRadii.sizes[corner].height == 0 || mBorderRadii.sizes[corner].width == 0) {
1489 0 : continue;
1490 : }
1491 0 : mBorderRadii.sizes[corner].width -= mBorderWidths[0] / 2;
1492 0 : mBorderRadii.sizes[corner].height -= mBorderWidths[0] / 2;
1493 : }
1494 :
1495 0 : mContext->NewPath();
1496 0 : mContext->RoundedRectangle(mOuterRect, mBorderRadii);
1497 0 : mContext->Stroke();
1498 0 : return;
1499 : }
1500 : }
1501 :
1502 : bool hasCompositeColors;
1503 :
1504 0 : allBordersSolid = AllBordersSolid(&hasCompositeColors);
1505 : // This leaves the border corners non-interpolated for single width borders.
1506 : // Doing this is slightly faster and shouldn't be a problem visually.
1507 0 : if (allBordersSolid &&
1508 : allBordersSameWidth &&
1509 0 : mCompositeColors[0] == NULL &&
1510 0 : mBorderWidths[0] == 1 &&
1511 : mNoBorderRadius &&
1512 0 : !mAvoidStroke)
1513 : {
1514 0 : DrawSingleWidthSolidBorder();
1515 0 : return;
1516 : }
1517 :
1518 0 : if (allBordersSolid && !hasCompositeColors &&
1519 0 : !mAvoidStroke)
1520 : {
1521 0 : DrawNoCompositeColorSolidBorder();
1522 0 : return;
1523 : }
1524 :
1525 0 : if (allBordersSolid &&
1526 : allBordersSameWidth &&
1527 : mNoBorderRadius &&
1528 0 : !mAvoidStroke)
1529 : {
1530 : // Easy enough to deal with.
1531 0 : DrawRectangularCompositeColors();
1532 0 : return;
1533 : }
1534 :
1535 : // If we have composite colors -and- border radius,
1536 : // then use separate corners so we get OPERATOR_ADD for the corners.
1537 : // Otherwise, we'll get artifacts as we draw stacked 1px-wide curves.
1538 0 : if (allBordersSame && mCompositeColors[0] != nsnull && !mNoBorderRadius)
1539 0 : forceSeparateCorners = true;
1540 :
1541 0 : S(" mOuterRect: "), S(mOuterRect), SN();
1542 0 : S(" mInnerRect: "), S(mInnerRect), SN();
1543 0 : SF(" mBorderColors: 0x%08x 0x%08x 0x%08x 0x%08x\n", mBorderColors[0], mBorderColors[1], mBorderColors[2], mBorderColors[3]);
1544 :
1545 : // if conditioning the outside rect failed, then bail -- the outside
1546 : // rect is supposed to enclose the entire border
1547 0 : mOuterRect.Condition();
1548 0 : if (mOuterRect.IsEmpty())
1549 0 : return;
1550 :
1551 0 : mInnerRect.Condition();
1552 0 : PRIntn dashedSides = 0;
1553 :
1554 0 : NS_FOR_CSS_SIDES(i) {
1555 0 : PRUint8 style = mBorderStyles[i];
1556 0 : if (style == NS_STYLE_BORDER_STYLE_DASHED ||
1557 : style == NS_STYLE_BORDER_STYLE_DOTTED)
1558 : {
1559 : // pretend that all borders aren't the same; we need to draw
1560 : // things separately for dashed/dotting
1561 0 : allBordersSame = false;
1562 0 : dashedSides |= (1 << i);
1563 : }
1564 : }
1565 :
1566 0 : SF(" allBordersSame: %d dashedSides: 0x%02x\n", allBordersSame, dashedSides);
1567 :
1568 0 : if (allBordersSame && !forceSeparateCorners) {
1569 : /* Draw everything in one go */
1570 0 : DrawBorderSides(SIDE_BITS_ALL);
1571 0 : SN("---------------- (1)");
1572 : } else {
1573 : /* We have more than one pass to go. Draw the corners separately from the sides. */
1574 :
1575 : /*
1576 : * If we have a 1px-wide border, the corners are going to be
1577 : * negligible, so don't bother doing anything fancy. Just extend
1578 : * the top and bottom borders to the right 1px and the left border
1579 : * to the bottom 1px. We do this by twiddling the corner dimensions,
1580 : * which causes the right to happen later on. Only do this if we have
1581 : * a 1.0 unit border all around and no border radius.
1582 : */
1583 :
1584 0 : NS_FOR_CSS_CORNERS(corner) {
1585 0 : const mozilla::css::Side sides[2] = { mozilla::css::Side(corner), PREV_SIDE(corner) };
1586 :
1587 0 : if (!IsZeroSize(mBorderRadii[corner]))
1588 0 : continue;
1589 :
1590 0 : if (mBorderWidths[sides[0]] == 1.0 && mBorderWidths[sides[1]] == 1.0) {
1591 0 : if (corner == NS_CORNER_TOP_LEFT || corner == NS_CORNER_TOP_RIGHT)
1592 0 : mBorderCornerDimensions[corner].width = 0.0;
1593 : else
1594 0 : mBorderCornerDimensions[corner].height = 0.0;
1595 : }
1596 : }
1597 :
1598 : // First, the corners
1599 0 : NS_FOR_CSS_CORNERS(corner) {
1600 : // if there's no corner, don't do all this work for it
1601 0 : if (IsZeroSize(mBorderCornerDimensions[corner]))
1602 0 : continue;
1603 :
1604 0 : const PRIntn sides[2] = { corner, PREV_SIDE(corner) };
1605 0 : PRIntn sideBits = (1 << sides[0]) | (1 << sides[1]);
1606 :
1607 0 : bool simpleCornerStyle = mCompositeColors[sides[0]] == NULL &&
1608 0 : mCompositeColors[sides[1]] == NULL &&
1609 0 : AreBorderSideFinalStylesSame(sideBits);
1610 :
1611 : // If we don't have anything complex going on in this corner,
1612 : // then we can just fill the corner with a solid color, and avoid
1613 : // the potentially expensive clip.
1614 0 : if (simpleCornerStyle &&
1615 0 : IsZeroSize(mBorderRadii[corner]) &&
1616 0 : IsSolidCornerStyle(mBorderStyles[sides[0]], corner))
1617 : {
1618 0 : mContext->NewPath();
1619 0 : DoCornerSubPath(corner);
1620 0 : mContext->SetColor(MakeBorderColor(mBorderColors[sides[0]],
1621 : mBackgroundColor,
1622 0 : BorderColorStyleForSolidCorner(mBorderStyles[sides[0]], corner)));
1623 0 : mContext->Fill();
1624 0 : continue;
1625 : }
1626 :
1627 0 : mContext->Save();
1628 :
1629 : // clip to the corner
1630 0 : mContext->NewPath();
1631 0 : DoCornerSubPath(corner);
1632 0 : mContext->Clip();
1633 :
1634 0 : if (simpleCornerStyle) {
1635 : // we don't need a group for this corner, the sides are the same,
1636 : // but we weren't able to render just a solid block for the corner.
1637 0 : DrawBorderSides(sideBits);
1638 : } else {
1639 : // Sides are different. We need to draw using OPERATOR_ADD to
1640 : // get correct color blending behaviour at the seam. We need
1641 : // to do it in an offscreen surface to ensure that we're
1642 : // always compositing on transparent black. If the colors
1643 : // don't have transparency and the current destination surface
1644 : // has an alpha channel, we could just clear the region and
1645 : // avoid the temporary, but that situation doesn't happen all
1646 : // that often in practice (we double buffer to no-alpha
1647 : // surfaces).
1648 :
1649 0 : mContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1650 0 : mContext->SetOperator(gfxContext::OPERATOR_ADD);
1651 :
1652 0 : for (int cornerSide = 0; cornerSide < 2; cornerSide++) {
1653 0 : mozilla::css::Side side = mozilla::css::Side(sides[cornerSide]);
1654 0 : PRUint8 style = mBorderStyles[side];
1655 :
1656 0 : SF("corner: %d cornerSide: %d side: %d style: %d\n", corner, cornerSide, side, style);
1657 :
1658 0 : mContext->Save();
1659 :
1660 0 : mContext->NewPath();
1661 0 : DoSideClipSubPath(side);
1662 0 : mContext->Clip();
1663 :
1664 0 : DrawBorderSides(1 << side);
1665 :
1666 0 : mContext->Restore();
1667 : }
1668 :
1669 0 : mContext->PopGroupToSource();
1670 0 : mContext->SetOperator(gfxContext::OPERATOR_OVER);
1671 0 : mContext->Paint();
1672 : }
1673 :
1674 0 : mContext->Restore();
1675 :
1676 0 : SN();
1677 : }
1678 :
1679 : // in the case of a single-unit border, we already munged the
1680 : // corners up above; so we can just draw the top left and bottom
1681 : // right sides separately, if they're the same.
1682 : //
1683 : // We need to check for mNoBorderRadius, because when there is
1684 : // one, FillSolidBorder always draws the full rounded rectangle
1685 : // and expects there to be a clip in place.
1686 0 : PRIntn alreadyDrawnSides = 0;
1687 0 : if (mOneUnitBorder &&
1688 : mNoBorderRadius &&
1689 : (dashedSides & (SIDE_BIT_TOP | SIDE_BIT_LEFT)) == 0)
1690 : {
1691 0 : if (tlBordersSame) {
1692 0 : DrawBorderSides(SIDE_BIT_TOP | SIDE_BIT_LEFT);
1693 0 : alreadyDrawnSides |= (SIDE_BIT_TOP | SIDE_BIT_LEFT);
1694 : }
1695 :
1696 0 : if (brBordersSame && (dashedSides & (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) == 0) {
1697 0 : DrawBorderSides(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT);
1698 0 : alreadyDrawnSides |= (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT);
1699 : }
1700 : }
1701 :
1702 : // We're done with the corners, now draw the sides.
1703 0 : NS_FOR_CSS_SIDES (side) {
1704 : // if we drew it above, skip it
1705 0 : if (alreadyDrawnSides & (1 << side))
1706 0 : continue;
1707 :
1708 : // If there's no border on this side, skip it
1709 0 : if (mBorderWidths[side] == 0.0 ||
1710 0 : mBorderStyles[side] == NS_STYLE_BORDER_STYLE_HIDDEN ||
1711 0 : mBorderStyles[side] == NS_STYLE_BORDER_STYLE_NONE)
1712 0 : continue;
1713 :
1714 :
1715 0 : if (dashedSides & (1 << side)) {
1716 : // Dashed sides will always draw just the part ignoring the
1717 : // corners for the side, so no need to clip.
1718 0 : DrawDashedSide (side);
1719 :
1720 0 : SN("---------------- (d)");
1721 0 : continue;
1722 : }
1723 :
1724 : // Undashed sides will currently draw the entire side,
1725 : // including parts that would normally be covered by a corner,
1726 : // so we need to clip.
1727 : //
1728 : // XXX Optimization -- it would be good to make this work like
1729 : // DrawDashedSide, and have a DrawOneSide function that just
1730 : // draws one side and not the corners, because then we can
1731 : // avoid the potentially expensive clip.
1732 0 : mContext->Save();
1733 0 : mContext->NewPath();
1734 0 : DoSideClipWithoutCornersSubPath(side);
1735 0 : mContext->Clip();
1736 :
1737 0 : DrawBorderSides(1 << side);
1738 :
1739 0 : mContext->Restore();
1740 :
1741 0 : SN("---------------- (*)");
1742 : }
1743 : }
1744 : }
|