1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsCSSRenderingBorders.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1941 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim:cindent:ts=2:et:sw=2: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsStyleConsts.h" 1.11 +#include "nsCSSColorUtils.h" 1.12 +#include "GeckoProfiler.h" 1.13 +#include "nsExpirationTracker.h" 1.14 +#include "RoundedRect.h" 1.15 +#include "nsClassHashtable.h" 1.16 +#include "nsStyleStruct.h" 1.17 +#include "gfxContext.h" 1.18 +#include "nsCSSRenderingBorders.h" 1.19 +#include "mozilla/gfx/2D.h" 1.20 +#include "gfx2DGlue.h" 1.21 +#include "gfxGradientCache.h" 1.22 +#include <algorithm> 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::gfx; 1.26 + 1.27 +/** 1.28 + * nsCSSRendering::PaintBorder 1.29 + * nsCSSRendering::PaintOutline 1.30 + * -> DrawBorders 1.31 + * 1.32 + * DrawBorders 1.33 + * -> Ability to use specialized approach? 1.34 + * |- Draw using specialized function 1.35 + * |- separate corners? 1.36 + * |- dashed side mask 1.37 + * | 1.38 + * -> can border be drawn in 1 pass? (e.g., solid border same color all around) 1.39 + * |- DrawBorderSides with all 4 sides 1.40 + * -> more than 1 pass? 1.41 + * |- for each corner 1.42 + * |- clip to DoCornerClipSubPath 1.43 + * |- for each side adjacent to corner 1.44 + * |- clip to DoSideClipSubPath 1.45 + * |- DrawBorderSides with one side 1.46 + * |- for each side 1.47 + * |- DoSideClipWithoutCornersSubPath 1.48 + * |- DrawDashedSide || DrawBorderSides with one side 1.49 + */ 1.50 + 1.51 +static void ComputeBorderCornerDimensions(const gfxRect& aOuterRect, 1.52 + const gfxRect& aInnerRect, 1.53 + const gfxCornerSizes& aRadii, 1.54 + gfxCornerSizes *aDimsResult); 1.55 + 1.56 +// given a side index, get the previous and next side index 1.57 +#define NEXT_SIDE(_s) mozilla::css::Side(((_s) + 1) & 3) 1.58 +#define PREV_SIDE(_s) mozilla::css::Side(((_s) + 3) & 3) 1.59 + 1.60 +// from the given base color and the background color, turn 1.61 +// color into a color for the given border pattern style 1.62 +static gfxRGBA MakeBorderColor(const gfxRGBA& aColor, 1.63 + const gfxRGBA& aBackgroundColor, 1.64 + BorderColorStyle aBorderColorStyle); 1.65 + 1.66 + 1.67 +// Given a line index (an index starting from the outside of the 1.68 +// border going inwards) and an array of line styles, calculate the 1.69 +// color that that stripe of the border should be rendered in. 1.70 +static gfxRGBA ComputeColorForLine(uint32_t aLineIndex, 1.71 + const BorderColorStyle* aBorderColorStyle, 1.72 + uint32_t aBorderColorStyleCount, 1.73 + nscolor aBorderColor, 1.74 + nscolor aBackgroundColor); 1.75 + 1.76 +static gfxRGBA ComputeCompositeColorForLine(uint32_t aLineIndex, 1.77 + const nsBorderColors* aBorderColors); 1.78 + 1.79 +// little helper function to check if the array of 4 floats given are 1.80 +// equal to the given value 1.81 +static bool 1.82 +CheckFourFloatsEqual(const gfxFloat *vals, gfxFloat k) 1.83 +{ 1.84 + return (vals[0] == k && 1.85 + vals[1] == k && 1.86 + vals[2] == k && 1.87 + vals[3] == k); 1.88 +} 1.89 + 1.90 +static bool 1.91 +IsZeroSize(const gfxSize& sz) { 1.92 + return sz.width == 0.0 || sz.height == 0.0; 1.93 +} 1.94 + 1.95 +static bool 1.96 +AllCornersZeroSize(const gfxCornerSizes& corners) { 1.97 + return IsZeroSize(corners[NS_CORNER_TOP_LEFT]) && 1.98 + IsZeroSize(corners[NS_CORNER_TOP_RIGHT]) && 1.99 + IsZeroSize(corners[NS_CORNER_BOTTOM_RIGHT]) && 1.100 + IsZeroSize(corners[NS_CORNER_BOTTOM_LEFT]); 1.101 +} 1.102 + 1.103 +typedef enum { 1.104 + // Normal solid square corner. Will be rectangular, the size of the 1.105 + // adjacent sides. If the corner has a border radius, the corner 1.106 + // will always be solid, since we don't do dotted/dashed etc. 1.107 + CORNER_NORMAL, 1.108 + 1.109 + // Paint the corner in whatever style is not dotted/dashed of the 1.110 + // adjacent corners. 1.111 + CORNER_SOLID, 1.112 + 1.113 + // Paint the corner as a dot, the size of the bigger of the adjacent 1.114 + // sides. 1.115 + CORNER_DOT 1.116 +} CornerStyle; 1.117 + 1.118 +nsCSSBorderRenderer::nsCSSBorderRenderer(int32_t aAppUnitsPerPixel, 1.119 + gfxContext* aDestContext, 1.120 + gfxRect& aOuterRect, 1.121 + const uint8_t* aBorderStyles, 1.122 + const gfxFloat* aBorderWidths, 1.123 + gfxCornerSizes& aBorderRadii, 1.124 + const nscolor* aBorderColors, 1.125 + nsBorderColors* const* aCompositeColors, 1.126 + int aSkipSides, 1.127 + nscolor aBackgroundColor) 1.128 + : mContext(aDestContext), 1.129 + mOuterRect(aOuterRect), 1.130 + mBorderStyles(aBorderStyles), 1.131 + mBorderWidths(aBorderWidths), 1.132 + mBorderRadii(aBorderRadii), 1.133 + mBorderColors(aBorderColors), 1.134 + mCompositeColors(aCompositeColors), 1.135 + mAUPP(aAppUnitsPerPixel), 1.136 + mSkipSides(aSkipSides), 1.137 + mBackgroundColor(aBackgroundColor) 1.138 +{ 1.139 + if (!mCompositeColors) { 1.140 + static nsBorderColors * const noColors[4] = { nullptr }; 1.141 + mCompositeColors = &noColors[0]; 1.142 + } 1.143 + 1.144 + mInnerRect = mOuterRect; 1.145 + mInnerRect.Deflate( 1.146 + gfxMargin(mBorderStyles[0] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[0] : 0, 1.147 + mBorderStyles[1] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[1] : 0, 1.148 + mBorderStyles[2] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[2] : 0, 1.149 + mBorderStyles[3] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[3] : 0)); 1.150 + 1.151 + ComputeBorderCornerDimensions(mOuterRect, mInnerRect, mBorderRadii, &mBorderCornerDimensions); 1.152 + 1.153 + mOneUnitBorder = CheckFourFloatsEqual(mBorderWidths, 1.0); 1.154 + mNoBorderRadius = AllCornersZeroSize(mBorderRadii); 1.155 + mAvoidStroke = false; 1.156 +} 1.157 + 1.158 +/* static */ void 1.159 +nsCSSBorderRenderer::ComputeInnerRadii(const gfxCornerSizes& aRadii, 1.160 + const gfxFloat *aBorderSizes, 1.161 + gfxCornerSizes *aInnerRadiiRet) 1.162 +{ 1.163 + gfxCornerSizes& iRadii = *aInnerRadiiRet; 1.164 + 1.165 + iRadii[C_TL].width = std::max(0.0, aRadii[C_TL].width - aBorderSizes[NS_SIDE_LEFT]); 1.166 + iRadii[C_TL].height = std::max(0.0, aRadii[C_TL].height - aBorderSizes[NS_SIDE_TOP]); 1.167 + 1.168 + iRadii[C_TR].width = std::max(0.0, aRadii[C_TR].width - aBorderSizes[NS_SIDE_RIGHT]); 1.169 + iRadii[C_TR].height = std::max(0.0, aRadii[C_TR].height - aBorderSizes[NS_SIDE_TOP]); 1.170 + 1.171 + iRadii[C_BR].width = std::max(0.0, aRadii[C_BR].width - aBorderSizes[NS_SIDE_RIGHT]); 1.172 + iRadii[C_BR].height = std::max(0.0, aRadii[C_BR].height - aBorderSizes[NS_SIDE_BOTTOM]); 1.173 + 1.174 + iRadii[C_BL].width = std::max(0.0, aRadii[C_BL].width - aBorderSizes[NS_SIDE_LEFT]); 1.175 + iRadii[C_BL].height = std::max(0.0, aRadii[C_BL].height - aBorderSizes[NS_SIDE_BOTTOM]); 1.176 +} 1.177 + 1.178 +/* static */ void 1.179 +nsCSSBorderRenderer::ComputeOuterRadii(const gfxCornerSizes& aRadii, 1.180 + const gfxFloat *aBorderSizes, 1.181 + gfxCornerSizes *aOuterRadiiRet) 1.182 +{ 1.183 + gfxCornerSizes& oRadii = *aOuterRadiiRet; 1.184 + 1.185 + // default all corners to sharp corners 1.186 + oRadii = gfxCornerSizes(0.0); 1.187 + 1.188 + // round the edges that have radii > 0.0 to start with 1.189 + if (aRadii[C_TL].width > 0.0 && aRadii[C_TL].height > 0.0) { 1.190 + oRadii[C_TL].width = std::max(0.0, aRadii[C_TL].width + aBorderSizes[NS_SIDE_LEFT]); 1.191 + oRadii[C_TL].height = std::max(0.0, aRadii[C_TL].height + aBorderSizes[NS_SIDE_TOP]); 1.192 + } 1.193 + 1.194 + if (aRadii[C_TR].width > 0.0 && aRadii[C_TR].height > 0.0) { 1.195 + oRadii[C_TR].width = std::max(0.0, aRadii[C_TR].width + aBorderSizes[NS_SIDE_RIGHT]); 1.196 + oRadii[C_TR].height = std::max(0.0, aRadii[C_TR].height + aBorderSizes[NS_SIDE_TOP]); 1.197 + } 1.198 + 1.199 + if (aRadii[C_BR].width > 0.0 && aRadii[C_BR].height > 0.0) { 1.200 + oRadii[C_BR].width = std::max(0.0, aRadii[C_BR].width + aBorderSizes[NS_SIDE_RIGHT]); 1.201 + oRadii[C_BR].height = std::max(0.0, aRadii[C_BR].height + aBorderSizes[NS_SIDE_BOTTOM]); 1.202 + } 1.203 + 1.204 + if (aRadii[C_BL].width > 0.0 && aRadii[C_BL].height > 0.0) { 1.205 + oRadii[C_BL].width = std::max(0.0, aRadii[C_BL].width + aBorderSizes[NS_SIDE_LEFT]); 1.206 + oRadii[C_BL].height = std::max(0.0, aRadii[C_BL].height + aBorderSizes[NS_SIDE_BOTTOM]); 1.207 + } 1.208 +} 1.209 + 1.210 +/*static*/ void 1.211 +ComputeBorderCornerDimensions(const gfxRect& aOuterRect, 1.212 + const gfxRect& aInnerRect, 1.213 + const gfxCornerSizes& aRadii, 1.214 + gfxCornerSizes *aDimsRet) 1.215 +{ 1.216 + gfxFloat leftWidth = aInnerRect.X() - aOuterRect.X(); 1.217 + gfxFloat topWidth = aInnerRect.Y() - aOuterRect.Y(); 1.218 + gfxFloat rightWidth = aOuterRect.Width() - aInnerRect.Width() - leftWidth; 1.219 + gfxFloat bottomWidth = aOuterRect.Height() - aInnerRect.Height() - topWidth; 1.220 + 1.221 + if (AllCornersZeroSize(aRadii)) { 1.222 + // These will always be in pixel units from CSS 1.223 + (*aDimsRet)[C_TL] = gfxSize(leftWidth, topWidth); 1.224 + (*aDimsRet)[C_TR] = gfxSize(rightWidth, topWidth); 1.225 + (*aDimsRet)[C_BR] = gfxSize(rightWidth, bottomWidth); 1.226 + (*aDimsRet)[C_BL] = gfxSize(leftWidth, bottomWidth); 1.227 + } else { 1.228 + // Always round up to whole pixels for the corners; it's safe to 1.229 + // make the corners bigger than necessary, and this way we ensure 1.230 + // that we avoid seams. 1.231 + (*aDimsRet)[C_TL] = gfxSize(ceil(std::max(leftWidth, aRadii[C_TL].width)), 1.232 + ceil(std::max(topWidth, aRadii[C_TL].height))); 1.233 + (*aDimsRet)[C_TR] = gfxSize(ceil(std::max(rightWidth, aRadii[C_TR].width)), 1.234 + ceil(std::max(topWidth, aRadii[C_TR].height))); 1.235 + (*aDimsRet)[C_BR] = gfxSize(ceil(std::max(rightWidth, aRadii[C_BR].width)), 1.236 + ceil(std::max(bottomWidth, aRadii[C_BR].height))); 1.237 + (*aDimsRet)[C_BL] = gfxSize(ceil(std::max(leftWidth, aRadii[C_BL].width)), 1.238 + ceil(std::max(bottomWidth, aRadii[C_BL].height))); 1.239 + } 1.240 +} 1.241 + 1.242 +bool 1.243 +nsCSSBorderRenderer::AreBorderSideFinalStylesSame(uint8_t aSides) 1.244 +{ 1.245 + NS_ASSERTION(aSides != 0 && (aSides & ~SIDE_BITS_ALL) == 0, 1.246 + "AreBorderSidesSame: invalid whichSides!"); 1.247 + 1.248 + /* First check if the specified styles and colors are the same for all sides */ 1.249 + int firstStyle = 0; 1.250 + NS_FOR_CSS_SIDES (i) { 1.251 + if (firstStyle == i) { 1.252 + if (((1 << i) & aSides) == 0) 1.253 + firstStyle++; 1.254 + continue; 1.255 + } 1.256 + 1.257 + if (((1 << i) & aSides) == 0) { 1.258 + continue; 1.259 + } 1.260 + 1.261 + if (mBorderStyles[firstStyle] != mBorderStyles[i] || 1.262 + mBorderColors[firstStyle] != mBorderColors[i] || 1.263 + !nsBorderColors::Equal(mCompositeColors[firstStyle], 1.264 + mCompositeColors[i])) 1.265 + return false; 1.266 + } 1.267 + 1.268 + /* Then if it's one of the two-tone styles and we're not 1.269 + * just comparing the TL or BR sides */ 1.270 + switch (mBorderStyles[firstStyle]) { 1.271 + case NS_STYLE_BORDER_STYLE_GROOVE: 1.272 + case NS_STYLE_BORDER_STYLE_RIDGE: 1.273 + case NS_STYLE_BORDER_STYLE_INSET: 1.274 + case NS_STYLE_BORDER_STYLE_OUTSET: 1.275 + return ((aSides & ~(SIDE_BIT_TOP | SIDE_BIT_LEFT)) == 0 || 1.276 + (aSides & ~(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) == 0); 1.277 + } 1.278 + 1.279 + return true; 1.280 +} 1.281 + 1.282 +bool 1.283 +nsCSSBorderRenderer::IsSolidCornerStyle(uint8_t aStyle, mozilla::css::Corner aCorner) 1.284 +{ 1.285 + switch (aStyle) { 1.286 + case NS_STYLE_BORDER_STYLE_DOTTED: 1.287 + case NS_STYLE_BORDER_STYLE_DASHED: 1.288 + case NS_STYLE_BORDER_STYLE_SOLID: 1.289 + return true; 1.290 + 1.291 + case NS_STYLE_BORDER_STYLE_INSET: 1.292 + case NS_STYLE_BORDER_STYLE_OUTSET: 1.293 + return (aCorner == NS_CORNER_TOP_LEFT || aCorner == NS_CORNER_BOTTOM_RIGHT); 1.294 + 1.295 + case NS_STYLE_BORDER_STYLE_GROOVE: 1.296 + case NS_STYLE_BORDER_STYLE_RIDGE: 1.297 + return mOneUnitBorder && (aCorner == NS_CORNER_TOP_LEFT || aCorner == NS_CORNER_BOTTOM_RIGHT); 1.298 + 1.299 + case NS_STYLE_BORDER_STYLE_DOUBLE: 1.300 + return mOneUnitBorder; 1.301 + 1.302 + default: 1.303 + return false; 1.304 + } 1.305 +} 1.306 + 1.307 +BorderColorStyle 1.308 +nsCSSBorderRenderer::BorderColorStyleForSolidCorner(uint8_t aStyle, mozilla::css::Corner aCorner) 1.309 +{ 1.310 + // note that this function assumes that the corner is already solid, 1.311 + // as per the earlier function 1.312 + switch (aStyle) { 1.313 + case NS_STYLE_BORDER_STYLE_DOTTED: 1.314 + case NS_STYLE_BORDER_STYLE_DASHED: 1.315 + case NS_STYLE_BORDER_STYLE_SOLID: 1.316 + case NS_STYLE_BORDER_STYLE_DOUBLE: 1.317 + return BorderColorStyleSolid; 1.318 + 1.319 + case NS_STYLE_BORDER_STYLE_INSET: 1.320 + case NS_STYLE_BORDER_STYLE_GROOVE: 1.321 + if (aCorner == NS_CORNER_TOP_LEFT) 1.322 + return BorderColorStyleDark; 1.323 + else if (aCorner == NS_CORNER_BOTTOM_RIGHT) 1.324 + return BorderColorStyleLight; 1.325 + break; 1.326 + 1.327 + case NS_STYLE_BORDER_STYLE_OUTSET: 1.328 + case NS_STYLE_BORDER_STYLE_RIDGE: 1.329 + if (aCorner == NS_CORNER_TOP_LEFT) 1.330 + return BorderColorStyleLight; 1.331 + else if (aCorner == NS_CORNER_BOTTOM_RIGHT) 1.332 + return BorderColorStyleDark; 1.333 + break; 1.334 + } 1.335 + 1.336 + return BorderColorStyleNone; 1.337 +} 1.338 + 1.339 +void 1.340 +nsCSSBorderRenderer::DoCornerSubPath(mozilla::css::Corner aCorner) 1.341 +{ 1.342 + gfxPoint offset(0.0, 0.0); 1.343 + 1.344 + if (aCorner == C_TR || aCorner == C_BR) 1.345 + offset.x = mOuterRect.Width() - mBorderCornerDimensions[aCorner].width; 1.346 + if (aCorner == C_BR || aCorner == C_BL) 1.347 + offset.y = mOuterRect.Height() - mBorderCornerDimensions[aCorner].height; 1.348 + 1.349 + mContext->Rectangle(gfxRect(mOuterRect.TopLeft() + offset, 1.350 + mBorderCornerDimensions[aCorner])); 1.351 +} 1.352 + 1.353 +void 1.354 +nsCSSBorderRenderer::DoSideClipWithoutCornersSubPath(mozilla::css::Side aSide) 1.355 +{ 1.356 + gfxPoint offset(0.0, 0.0); 1.357 + 1.358 + // The offset from the outside rect to the start of this side's 1.359 + // box. For the top and bottom sides, the height of the box 1.360 + // must be the border height; the x start must take into account 1.361 + // the corner size (which may be bigger than the right or left 1.362 + // side's width). The same applies to the right and left sides. 1.363 + if (aSide == NS_SIDE_TOP) { 1.364 + offset.x = mBorderCornerDimensions[C_TL].width; 1.365 + } else if (aSide == NS_SIDE_RIGHT) { 1.366 + offset.x = mOuterRect.Width() - mBorderWidths[NS_SIDE_RIGHT]; 1.367 + offset.y = mBorderCornerDimensions[C_TR].height; 1.368 + } else if (aSide == NS_SIDE_BOTTOM) { 1.369 + offset.x = mBorderCornerDimensions[C_BL].width; 1.370 + offset.y = mOuterRect.Height() - mBorderWidths[NS_SIDE_BOTTOM]; 1.371 + } else if (aSide == NS_SIDE_LEFT) { 1.372 + offset.y = mBorderCornerDimensions[C_TL].height; 1.373 + } 1.374 + 1.375 + // The sum of the width & height of the corners adjacent to the 1.376 + // side. This relies on the relationship between side indexing and 1.377 + // corner indexing; that is, 0 == SIDE_TOP and 0 == CORNER_TOP_LEFT, 1.378 + // with both proceeding clockwise. 1.379 + gfxSize sideCornerSum = mBorderCornerDimensions[mozilla::css::Corner(aSide)] 1.380 + + mBorderCornerDimensions[mozilla::css::Corner(NEXT_SIDE(aSide))]; 1.381 + gfxRect rect(mOuterRect.TopLeft() + offset, 1.382 + mOuterRect.Size() - sideCornerSum); 1.383 + 1.384 + if (aSide == NS_SIDE_TOP || aSide == NS_SIDE_BOTTOM) 1.385 + rect.height = mBorderWidths[aSide]; 1.386 + else 1.387 + rect.width = mBorderWidths[aSide]; 1.388 + 1.389 + mContext->Rectangle(rect); 1.390 +} 1.391 + 1.392 +// The side border type and the adjacent border types are 1.393 +// examined and one of the different types of clipping (listed 1.394 +// below) is selected. 1.395 + 1.396 +typedef enum { 1.397 + // clip to the trapezoid formed by the corners of the 1.398 + // inner and outer rectangles for the given side 1.399 + SIDE_CLIP_TRAPEZOID, 1.400 + 1.401 + // clip to the trapezoid formed by the outer rectangle 1.402 + // corners and the center of the region, making sure 1.403 + // that diagonal lines all go directly from the outside 1.404 + // corner to the inside corner, but that they then continue on 1.405 + // to the middle. 1.406 + // 1.407 + // This is needed for correctly clipping rounded borders, 1.408 + // which might extend past the SIDE_CLIP_TRAPEZOID trap. 1.409 + SIDE_CLIP_TRAPEZOID_FULL, 1.410 + 1.411 + // clip to the rectangle formed by the given side; a specific 1.412 + // overlap algorithm is used; see the function for details. 1.413 + // this is currently used for dashing. 1.414 + SIDE_CLIP_RECTANGLE 1.415 +} SideClipType; 1.416 + 1.417 +// Given three points, p0, p1, and midPoint, move p1 further in to the 1.418 +// rectangle (of which aMidPoint is the center) so that it reaches the 1.419 +// closer of the horizontal or vertical lines intersecting the midpoint, 1.420 +// while maintaing the slope of the line. If p0 and p1 are the same, 1.421 +// just move p1 to midPoint (since there's no slope to maintain). 1.422 +// FIXME: Extending only to the midpoint isn't actually sufficient for 1.423 +// boxes with asymmetric radii. 1.424 +static void 1.425 +MaybeMoveToMidPoint(gfxPoint& aP0, gfxPoint& aP1, const gfxPoint& aMidPoint) 1.426 +{ 1.427 + gfxPoint ps = aP1 - aP0; 1.428 + 1.429 + if (ps.x == 0.0) { 1.430 + if (ps.y == 0.0) { 1.431 + aP1 = aMidPoint; 1.432 + } else { 1.433 + aP1.y = aMidPoint.y; 1.434 + } 1.435 + } else { 1.436 + if (ps.y == 0.0) { 1.437 + aP1.x = aMidPoint.x; 1.438 + } else { 1.439 + gfxFloat k = std::min((aMidPoint.x - aP0.x) / ps.x, 1.440 + (aMidPoint.y - aP0.y) / ps.y); 1.441 + aP1 = aP0 + ps * k; 1.442 + } 1.443 + } 1.444 +} 1.445 + 1.446 +void 1.447 +nsCSSBorderRenderer::DoSideClipSubPath(mozilla::css::Side aSide) 1.448 +{ 1.449 + // the clip proceeds clockwise from the top left corner; 1.450 + // so "start" in each case is the start of the region from that side. 1.451 + // 1.452 + // the final path will be formed like: 1.453 + // s0 ------- e0 1.454 + // | / 1.455 + // s1 ----- e1 1.456 + // 1.457 + // that is, the second point will always be on the inside 1.458 + 1.459 + gfxPoint start[2]; 1.460 + gfxPoint end[2]; 1.461 + 1.462 +#define IS_DASHED_OR_DOTTED(_s) ((_s) == NS_STYLE_BORDER_STYLE_DASHED || (_s) == NS_STYLE_BORDER_STYLE_DOTTED) 1.463 + bool isDashed = IS_DASHED_OR_DOTTED(mBorderStyles[aSide]); 1.464 + bool startIsDashed = IS_DASHED_OR_DOTTED(mBorderStyles[PREV_SIDE(aSide)]); 1.465 + bool endIsDashed = IS_DASHED_OR_DOTTED(mBorderStyles[NEXT_SIDE(aSide)]); 1.466 +#undef IS_DASHED_OR_DOTTED 1.467 + 1.468 + SideClipType startType = SIDE_CLIP_TRAPEZOID; 1.469 + SideClipType endType = SIDE_CLIP_TRAPEZOID; 1.470 + 1.471 + if (!IsZeroSize(mBorderRadii[mozilla::css::Corner(aSide)])) 1.472 + startType = SIDE_CLIP_TRAPEZOID_FULL; 1.473 + else if (startIsDashed && isDashed) 1.474 + startType = SIDE_CLIP_RECTANGLE; 1.475 + 1.476 + if (!IsZeroSize(mBorderRadii[mozilla::css::Corner(NEXT_SIDE(aSide))])) 1.477 + endType = SIDE_CLIP_TRAPEZOID_FULL; 1.478 + else if (endIsDashed && isDashed) 1.479 + endType = SIDE_CLIP_RECTANGLE; 1.480 + 1.481 + gfxPoint midPoint = mInnerRect.Center(); 1.482 + 1.483 + start[0] = mOuterRect.CCWCorner(aSide); 1.484 + start[1] = mInnerRect.CCWCorner(aSide); 1.485 + 1.486 + end[0] = mOuterRect.CWCorner(aSide); 1.487 + end[1] = mInnerRect.CWCorner(aSide); 1.488 + 1.489 + if (startType == SIDE_CLIP_TRAPEZOID_FULL) { 1.490 + MaybeMoveToMidPoint(start[0], start[1], midPoint); 1.491 + } else if (startType == SIDE_CLIP_RECTANGLE) { 1.492 + if (aSide == NS_SIDE_TOP || aSide == NS_SIDE_BOTTOM) 1.493 + start[1] = gfxPoint(mOuterRect.CCWCorner(aSide).x, mInnerRect.CCWCorner(aSide).y); 1.494 + else 1.495 + start[1] = gfxPoint(mInnerRect.CCWCorner(aSide).x, mOuterRect.CCWCorner(aSide).y); 1.496 + } 1.497 + 1.498 + if (endType == SIDE_CLIP_TRAPEZOID_FULL) { 1.499 + MaybeMoveToMidPoint(end[0], end[1], midPoint); 1.500 + } else if (endType == SIDE_CLIP_RECTANGLE) { 1.501 + if (aSide == NS_SIDE_TOP || aSide == NS_SIDE_BOTTOM) 1.502 + end[0] = gfxPoint(mInnerRect.CWCorner(aSide).x, mOuterRect.CWCorner(aSide).y); 1.503 + else 1.504 + end[0] = gfxPoint(mOuterRect.CWCorner(aSide).x, mInnerRect.CWCorner(aSide).y); 1.505 + } 1.506 + 1.507 + mContext->MoveTo(start[0]); 1.508 + mContext->LineTo(end[0]); 1.509 + mContext->LineTo(end[1]); 1.510 + mContext->LineTo(start[1]); 1.511 + mContext->ClosePath(); 1.512 +} 1.513 + 1.514 +void 1.515 +nsCSSBorderRenderer::FillSolidBorder(const gfxRect& aOuterRect, 1.516 + const gfxRect& aInnerRect, 1.517 + const gfxCornerSizes& aBorderRadii, 1.518 + const gfxFloat *aBorderSizes, 1.519 + int aSides, 1.520 + const gfxRGBA& aColor) 1.521 +{ 1.522 + mContext->SetColor(aColor); 1.523 + // Note that this function is allowed to draw more than just the 1.524 + // requested sides. 1.525 + 1.526 + // If we have a border radius, do full rounded rectangles 1.527 + // and fill, regardless of what sides we're asked to draw. 1.528 + if (!AllCornersZeroSize(aBorderRadii)) { 1.529 + gfxCornerSizes innerRadii; 1.530 + ComputeInnerRadii(aBorderRadii, aBorderSizes, &innerRadii); 1.531 + 1.532 + mContext->NewPath(); 1.533 + 1.534 + // do the outer border 1.535 + mContext->RoundedRectangle(aOuterRect, aBorderRadii, true); 1.536 + 1.537 + // then do the inner border CCW 1.538 + mContext->RoundedRectangle(aInnerRect, innerRadii, false); 1.539 + 1.540 + mContext->Fill(); 1.541 + 1.542 + return; 1.543 + } 1.544 + 1.545 + // If we're asked to draw all sides of an equal-sized border, 1.546 + // stroking is fastest. This is a fairly common path, but partial 1.547 + // sides is probably second in the list -- there are a bunch of 1.548 + // common border styles, such as inset and outset, that are 1.549 + // top-left/bottom-right split. 1.550 + if (aSides == SIDE_BITS_ALL && 1.551 + CheckFourFloatsEqual(aBorderSizes, aBorderSizes[0]) && 1.552 + !mAvoidStroke) 1.553 + { 1.554 + gfxRect r(aOuterRect); 1.555 + r.Deflate(aBorderSizes[0] / 2.0); 1.556 + mContext->SetLineWidth(aBorderSizes[0]); 1.557 + 1.558 + mContext->NewPath(); 1.559 + mContext->Rectangle(r); 1.560 + mContext->Stroke(); 1.561 + 1.562 + return; 1.563 + } 1.564 + 1.565 + // Otherwise, we have unequal sized borders or we're only 1.566 + // drawing some sides; create rectangles for each side 1.567 + // and fill them. 1.568 + 1.569 + gfxRect r[4]; 1.570 + 1.571 + // compute base rects for each side 1.572 + if (aSides & SIDE_BIT_TOP) { 1.573 + r[NS_SIDE_TOP] = 1.574 + gfxRect(aOuterRect.X(), aOuterRect.Y(), 1.575 + aOuterRect.Width(), aBorderSizes[NS_SIDE_TOP]); 1.576 + } 1.577 + 1.578 + if (aSides & SIDE_BIT_BOTTOM) { 1.579 + r[NS_SIDE_BOTTOM] = 1.580 + gfxRect(aOuterRect.X(), aOuterRect.YMost() - aBorderSizes[NS_SIDE_BOTTOM], 1.581 + aOuterRect.Width(), aBorderSizes[NS_SIDE_BOTTOM]); 1.582 + } 1.583 + 1.584 + if (aSides & SIDE_BIT_LEFT) { 1.585 + r[NS_SIDE_LEFT] = 1.586 + gfxRect(aOuterRect.X(), aOuterRect.Y(), 1.587 + aBorderSizes[NS_SIDE_LEFT], aOuterRect.Height()); 1.588 + } 1.589 + 1.590 + if (aSides & SIDE_BIT_RIGHT) { 1.591 + r[NS_SIDE_RIGHT] = 1.592 + gfxRect(aOuterRect.XMost() - aBorderSizes[NS_SIDE_RIGHT], aOuterRect.Y(), 1.593 + aBorderSizes[NS_SIDE_RIGHT], aOuterRect.Height()); 1.594 + } 1.595 + 1.596 + // If two sides meet at a corner that we're rendering, then 1.597 + // make sure that we adjust one of the sides to avoid overlap. 1.598 + // This is especially important in the case of colors with 1.599 + // an alpha channel. 1.600 + 1.601 + if ((aSides & (SIDE_BIT_TOP | SIDE_BIT_LEFT)) == (SIDE_BIT_TOP | SIDE_BIT_LEFT)) { 1.602 + // adjust the left's top down a bit 1.603 + r[NS_SIDE_LEFT].y += aBorderSizes[NS_SIDE_TOP]; 1.604 + r[NS_SIDE_LEFT].height -= aBorderSizes[NS_SIDE_TOP]; 1.605 + } 1.606 + 1.607 + if ((aSides & (SIDE_BIT_TOP | SIDE_BIT_RIGHT)) == (SIDE_BIT_TOP | SIDE_BIT_RIGHT)) { 1.608 + // adjust the top's left a bit 1.609 + r[NS_SIDE_TOP].width -= aBorderSizes[NS_SIDE_RIGHT]; 1.610 + } 1.611 + 1.612 + if ((aSides & (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) == (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) { 1.613 + // adjust the right's bottom a bit 1.614 + r[NS_SIDE_RIGHT].height -= aBorderSizes[NS_SIDE_BOTTOM]; 1.615 + } 1.616 + 1.617 + if ((aSides & (SIDE_BIT_BOTTOM | SIDE_BIT_LEFT)) == (SIDE_BIT_BOTTOM | SIDE_BIT_LEFT)) { 1.618 + // adjust the bottom's left a bit 1.619 + r[NS_SIDE_BOTTOM].x += aBorderSizes[NS_SIDE_LEFT]; 1.620 + r[NS_SIDE_BOTTOM].width -= aBorderSizes[NS_SIDE_LEFT]; 1.621 + } 1.622 + 1.623 + // Filling these one by one is faster than filling them all at once. 1.624 + for (uint32_t i = 0; i < 4; i++) { 1.625 + if (aSides & (1 << i)) { 1.626 + mContext->NewPath(); 1.627 + mContext->Rectangle(r[i], true); 1.628 + mContext->Fill(); 1.629 + } 1.630 + } 1.631 +} 1.632 + 1.633 +gfxRGBA 1.634 +MakeBorderColor(const gfxRGBA& aColor, const gfxRGBA& aBackgroundColor, BorderColorStyle aBorderColorStyle) 1.635 +{ 1.636 + nscolor colors[2]; 1.637 + int k = 0; 1.638 + 1.639 + switch (aBorderColorStyle) { 1.640 + case BorderColorStyleNone: 1.641 + return gfxRGBA(0.0, 0.0, 0.0, 0.0); 1.642 + 1.643 + case BorderColorStyleLight: 1.644 + k = 1; 1.645 + /* fall through */ 1.646 + case BorderColorStyleDark: 1.647 + NS_GetSpecial3DColors(colors, aBackgroundColor.Packed(), aColor.Packed()); 1.648 + return gfxRGBA(colors[k]); 1.649 + 1.650 + case BorderColorStyleSolid: 1.651 + default: 1.652 + return aColor; 1.653 + } 1.654 +} 1.655 + 1.656 +gfxRGBA 1.657 +ComputeColorForLine(uint32_t aLineIndex, 1.658 + const BorderColorStyle* aBorderColorStyle, 1.659 + uint32_t aBorderColorStyleCount, 1.660 + nscolor aBorderColor, 1.661 + nscolor aBackgroundColor) 1.662 +{ 1.663 + NS_ASSERTION(aLineIndex < aBorderColorStyleCount, "Invalid lineIndex given"); 1.664 + 1.665 + return MakeBorderColor(gfxRGBA(aBorderColor), gfxRGBA(aBackgroundColor), aBorderColorStyle[aLineIndex]); 1.666 +} 1.667 + 1.668 +gfxRGBA 1.669 +ComputeCompositeColorForLine(uint32_t aLineIndex, 1.670 + const nsBorderColors* aBorderColors) 1.671 +{ 1.672 + while (aLineIndex-- && aBorderColors->mNext) 1.673 + aBorderColors = aBorderColors->mNext; 1.674 + 1.675 + return gfxRGBA(aBorderColors->mColor); 1.676 +} 1.677 + 1.678 +void 1.679 +nsCSSBorderRenderer::DrawBorderSidesCompositeColors(int aSides, const nsBorderColors *aCompositeColors) 1.680 +{ 1.681 + gfxCornerSizes radii = mBorderRadii; 1.682 + 1.683 + // the generic composite colors path; each border is 1px in size 1.684 + gfxRect soRect = mOuterRect; 1.685 + gfxFloat maxBorderWidth = 0; 1.686 + NS_FOR_CSS_SIDES (i) { 1.687 + maxBorderWidth = std::max(maxBorderWidth, mBorderWidths[i]); 1.688 + } 1.689 + 1.690 + gfxFloat fakeBorderSizes[4]; 1.691 + 1.692 + gfxPoint itl = mInnerRect.TopLeft(); 1.693 + gfxPoint ibr = mInnerRect.BottomRight(); 1.694 + 1.695 + for (uint32_t i = 0; i < uint32_t(maxBorderWidth); i++) { 1.696 + gfxRGBA lineColor = ComputeCompositeColorForLine(i, aCompositeColors); 1.697 + 1.698 + gfxRect siRect = soRect; 1.699 + siRect.Deflate(1.0); 1.700 + 1.701 + // now cap the rects to the real mInnerRect 1.702 + gfxPoint tl = siRect.TopLeft(); 1.703 + gfxPoint br = siRect.BottomRight(); 1.704 + 1.705 + tl.x = std::min(tl.x, itl.x); 1.706 + tl.y = std::min(tl.y, itl.y); 1.707 + 1.708 + br.x = std::max(br.x, ibr.x); 1.709 + br.y = std::max(br.y, ibr.y); 1.710 + 1.711 + siRect = gfxRect(tl.x, tl.y, br.x - tl.x , br.y - tl.y); 1.712 + 1.713 + fakeBorderSizes[NS_SIDE_TOP] = siRect.TopLeft().y - soRect.TopLeft().y; 1.714 + fakeBorderSizes[NS_SIDE_RIGHT] = soRect.TopRight().x - siRect.TopRight().x; 1.715 + fakeBorderSizes[NS_SIDE_BOTTOM] = soRect.BottomRight().y - siRect.BottomRight().y; 1.716 + fakeBorderSizes[NS_SIDE_LEFT] = siRect.BottomLeft().x - soRect.BottomLeft().x; 1.717 + 1.718 + FillSolidBorder(soRect, siRect, radii, fakeBorderSizes, aSides, lineColor); 1.719 + 1.720 + soRect = siRect; 1.721 + 1.722 + ComputeInnerRadii(radii, fakeBorderSizes, &radii); 1.723 + } 1.724 +} 1.725 + 1.726 +void 1.727 +nsCSSBorderRenderer::DrawBorderSides(int aSides) 1.728 +{ 1.729 + if (aSides == 0 || (aSides & ~SIDE_BITS_ALL) != 0) { 1.730 + NS_WARNING("DrawBorderSides: invalid sides!"); 1.731 + return; 1.732 + } 1.733 + 1.734 + uint8_t borderRenderStyle = NS_STYLE_BORDER_STYLE_NONE; 1.735 + nscolor borderRenderColor; 1.736 + const nsBorderColors *compositeColors = nullptr; 1.737 + 1.738 + uint32_t borderColorStyleCount = 0; 1.739 + BorderColorStyle borderColorStyleTopLeft[3], borderColorStyleBottomRight[3]; 1.740 + BorderColorStyle *borderColorStyle = nullptr; 1.741 + 1.742 + NS_FOR_CSS_SIDES (i) { 1.743 + if ((aSides & (1 << i)) == 0) 1.744 + continue; 1.745 + borderRenderStyle = mBorderStyles[i]; 1.746 + borderRenderColor = mBorderColors[i]; 1.747 + compositeColors = mCompositeColors[i]; 1.748 + break; 1.749 + } 1.750 + 1.751 + if (borderRenderStyle == NS_STYLE_BORDER_STYLE_NONE || 1.752 + borderRenderStyle == NS_STYLE_BORDER_STYLE_HIDDEN) 1.753 + return; 1.754 + 1.755 + // -moz-border-colors is a hack; if we have it for a border, then 1.756 + // it's always drawn solid, and each color is given 1px. The last 1.757 + // color is used for the remainder of the border's size. Just 1.758 + // hand off to another function to do all that. 1.759 + if (compositeColors) { 1.760 + DrawBorderSidesCompositeColors(aSides, compositeColors); 1.761 + return; 1.762 + } 1.763 + 1.764 + // We're not doing compositeColors, so we can calculate the 1.765 + // borderColorStyle based on the specified style. The 1.766 + // borderColorStyle array goes from the outer to the inner style. 1.767 + // 1.768 + // If the border width is 1, we need to change the borderRenderStyle 1.769 + // a bit to make sure that we get the right colors -- e.g. 'ridge' 1.770 + // with a 1px border needs to look like solid, not like 'outset'. 1.771 + if (mOneUnitBorder && 1.772 + (borderRenderStyle == NS_STYLE_BORDER_STYLE_RIDGE || 1.773 + borderRenderStyle == NS_STYLE_BORDER_STYLE_GROOVE || 1.774 + borderRenderStyle == NS_STYLE_BORDER_STYLE_DOUBLE)) 1.775 + borderRenderStyle = NS_STYLE_BORDER_STYLE_SOLID; 1.776 + 1.777 + switch (borderRenderStyle) { 1.778 + case NS_STYLE_BORDER_STYLE_SOLID: 1.779 + case NS_STYLE_BORDER_STYLE_DASHED: 1.780 + case NS_STYLE_BORDER_STYLE_DOTTED: 1.781 + borderColorStyleTopLeft[0] = BorderColorStyleSolid; 1.782 + 1.783 + borderColorStyleBottomRight[0] = BorderColorStyleSolid; 1.784 + 1.785 + borderColorStyleCount = 1; 1.786 + break; 1.787 + 1.788 + case NS_STYLE_BORDER_STYLE_GROOVE: 1.789 + borderColorStyleTopLeft[0] = BorderColorStyleDark; 1.790 + borderColorStyleTopLeft[1] = BorderColorStyleLight; 1.791 + 1.792 + borderColorStyleBottomRight[0] = BorderColorStyleLight; 1.793 + borderColorStyleBottomRight[1] = BorderColorStyleDark; 1.794 + 1.795 + borderColorStyleCount = 2; 1.796 + break; 1.797 + 1.798 + case NS_STYLE_BORDER_STYLE_RIDGE: 1.799 + borderColorStyleTopLeft[0] = BorderColorStyleLight; 1.800 + borderColorStyleTopLeft[1] = BorderColorStyleDark; 1.801 + 1.802 + borderColorStyleBottomRight[0] = BorderColorStyleDark; 1.803 + borderColorStyleBottomRight[1] = BorderColorStyleLight; 1.804 + 1.805 + borderColorStyleCount = 2; 1.806 + break; 1.807 + 1.808 + case NS_STYLE_BORDER_STYLE_DOUBLE: 1.809 + borderColorStyleTopLeft[0] = BorderColorStyleSolid; 1.810 + borderColorStyleTopLeft[1] = BorderColorStyleNone; 1.811 + borderColorStyleTopLeft[2] = BorderColorStyleSolid; 1.812 + 1.813 + borderColorStyleBottomRight[0] = BorderColorStyleSolid; 1.814 + borderColorStyleBottomRight[1] = BorderColorStyleNone; 1.815 + borderColorStyleBottomRight[2] = BorderColorStyleSolid; 1.816 + 1.817 + borderColorStyleCount = 3; 1.818 + break; 1.819 + 1.820 + case NS_STYLE_BORDER_STYLE_INSET: 1.821 + borderColorStyleTopLeft[0] = BorderColorStyleDark; 1.822 + borderColorStyleBottomRight[0] = BorderColorStyleLight; 1.823 + 1.824 + borderColorStyleCount = 1; 1.825 + break; 1.826 + 1.827 + case NS_STYLE_BORDER_STYLE_OUTSET: 1.828 + borderColorStyleTopLeft[0] = BorderColorStyleLight; 1.829 + borderColorStyleBottomRight[0] = BorderColorStyleDark; 1.830 + 1.831 + borderColorStyleCount = 1; 1.832 + break; 1.833 + 1.834 + default: 1.835 + NS_NOTREACHED("Unhandled border style!!"); 1.836 + break; 1.837 + } 1.838 + 1.839 + // The only way to get to here is by having a 1.840 + // borderColorStyleCount < 1 or > 3; this should never happen, 1.841 + // since -moz-border-colors doesn't get handled here. 1.842 + NS_ASSERTION(borderColorStyleCount > 0 && borderColorStyleCount < 4, 1.843 + "Non-border-colors case with borderColorStyleCount < 1 or > 3; what happened?"); 1.844 + 1.845 + // The caller should never give us anything with a mix 1.846 + // of TL/BR if the border style would require a 1.847 + // TL/BR split. 1.848 + if (aSides & (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) 1.849 + borderColorStyle = borderColorStyleBottomRight; 1.850 + else 1.851 + borderColorStyle = borderColorStyleTopLeft; 1.852 + 1.853 + // Distribute the border across the available space. 1.854 + gfxFloat borderWidths[3][4]; 1.855 + 1.856 + if (borderColorStyleCount == 1) { 1.857 + NS_FOR_CSS_SIDES (i) { 1.858 + borderWidths[0][i] = mBorderWidths[i]; 1.859 + } 1.860 + } else if (borderColorStyleCount == 2) { 1.861 + // with 2 color styles, any extra pixel goes to the outside 1.862 + NS_FOR_CSS_SIDES (i) { 1.863 + borderWidths[0][i] = int32_t(mBorderWidths[i]) / 2 + int32_t(mBorderWidths[i]) % 2; 1.864 + borderWidths[1][i] = int32_t(mBorderWidths[i]) / 2; 1.865 + } 1.866 + } else if (borderColorStyleCount == 3) { 1.867 + // with 3 color styles, any extra pixel (or lack of extra pixel) 1.868 + // goes to the middle 1.869 + NS_FOR_CSS_SIDES (i) { 1.870 + if (mBorderWidths[i] == 1.0) { 1.871 + borderWidths[0][i] = 1.0; 1.872 + borderWidths[1][i] = borderWidths[2][i] = 0.0; 1.873 + } else { 1.874 + int32_t rest = int32_t(mBorderWidths[i]) % 3; 1.875 + borderWidths[0][i] = borderWidths[2][i] = borderWidths[1][i] = (int32_t(mBorderWidths[i]) - rest) / 3; 1.876 + 1.877 + if (rest == 1) { 1.878 + borderWidths[1][i] += 1.0; 1.879 + } else if (rest == 2) { 1.880 + borderWidths[0][i] += 1.0; 1.881 + borderWidths[2][i] += 1.0; 1.882 + } 1.883 + } 1.884 + } 1.885 + } 1.886 + 1.887 + // make a copy that we can modify 1.888 + gfxCornerSizes radii = mBorderRadii; 1.889 + 1.890 + gfxRect soRect(mOuterRect); 1.891 + gfxRect siRect(mOuterRect); 1.892 + 1.893 + for (unsigned int i = 0; i < borderColorStyleCount; i++) { 1.894 + // walk siRect inwards at the start of the loop to get the 1.895 + // correct inner rect. 1.896 + siRect.Deflate(gfxMargin(borderWidths[i][0], borderWidths[i][1], 1.897 + borderWidths[i][2], borderWidths[i][3])); 1.898 + 1.899 + if (borderColorStyle[i] != BorderColorStyleNone) { 1.900 + gfxRGBA color = ComputeColorForLine(i, 1.901 + borderColorStyle, borderColorStyleCount, 1.902 + borderRenderColor, mBackgroundColor); 1.903 + 1.904 + FillSolidBorder(soRect, siRect, radii, borderWidths[i], aSides, color); 1.905 + } 1.906 + 1.907 + ComputeInnerRadii(radii, borderWidths[i], &radii); 1.908 + 1.909 + // And now soRect is the same as siRect, for the next line in. 1.910 + soRect = siRect; 1.911 + } 1.912 +} 1.913 + 1.914 +void 1.915 +nsCSSBorderRenderer::DrawDashedSide(mozilla::css::Side aSide) 1.916 +{ 1.917 + gfxFloat dashWidth; 1.918 + gfxFloat dash[2]; 1.919 + 1.920 + uint8_t style = mBorderStyles[aSide]; 1.921 + gfxFloat borderWidth = mBorderWidths[aSide]; 1.922 + nscolor borderColor = mBorderColors[aSide]; 1.923 + 1.924 + if (borderWidth == 0.0) 1.925 + return; 1.926 + 1.927 + if (style == NS_STYLE_BORDER_STYLE_NONE || 1.928 + style == NS_STYLE_BORDER_STYLE_HIDDEN) 1.929 + return; 1.930 + 1.931 + if (style == NS_STYLE_BORDER_STYLE_DASHED) { 1.932 + dashWidth = gfxFloat(borderWidth * DOT_LENGTH * DASH_LENGTH); 1.933 + 1.934 + dash[0] = dashWidth; 1.935 + dash[1] = dashWidth; 1.936 + 1.937 + mContext->SetLineCap(gfxContext::LINE_CAP_BUTT); 1.938 + } else if (style == NS_STYLE_BORDER_STYLE_DOTTED) { 1.939 + dashWidth = gfxFloat(borderWidth * DOT_LENGTH); 1.940 + 1.941 + if (borderWidth > 2.0) { 1.942 + dash[0] = 0.0; 1.943 + dash[1] = dashWidth * 2.0; 1.944 + 1.945 + mContext->SetLineCap(gfxContext::LINE_CAP_ROUND); 1.946 + } else { 1.947 + dash[0] = dashWidth; 1.948 + dash[1] = dashWidth; 1.949 + } 1.950 + } else { 1.951 + SF("DrawDashedSide: style: %d!!\n", style); 1.952 + NS_ERROR("DrawDashedSide called with style other than DASHED or DOTTED; someone's not playing nice"); 1.953 + return; 1.954 + } 1.955 + 1.956 + SF("dash: %f %f\n", dash[0], dash[1]); 1.957 + 1.958 + mContext->SetDash(dash, 2, 0.0); 1.959 + 1.960 + gfxPoint start = mOuterRect.CCWCorner(aSide); 1.961 + gfxPoint end = mOuterRect.CWCorner(aSide); 1.962 + 1.963 + if (aSide == NS_SIDE_TOP) { 1.964 + start.x += mBorderCornerDimensions[C_TL].width; 1.965 + end.x -= mBorderCornerDimensions[C_TR].width; 1.966 + 1.967 + start.y += borderWidth / 2.0; 1.968 + end.y += borderWidth / 2.0; 1.969 + } else if (aSide == NS_SIDE_RIGHT) { 1.970 + start.x -= borderWidth / 2.0; 1.971 + end.x -= borderWidth / 2.0; 1.972 + 1.973 + start.y += mBorderCornerDimensions[C_TR].height; 1.974 + end.y -= mBorderCornerDimensions[C_BR].height; 1.975 + } else if (aSide == NS_SIDE_BOTTOM) { 1.976 + start.x -= mBorderCornerDimensions[C_BR].width; 1.977 + end.x += mBorderCornerDimensions[C_BL].width; 1.978 + 1.979 + start.y -= borderWidth / 2.0; 1.980 + end.y -= borderWidth / 2.0; 1.981 + } else if (aSide == NS_SIDE_LEFT) { 1.982 + start.x += borderWidth / 2.0; 1.983 + end.x += borderWidth / 2.0; 1.984 + 1.985 + start.y -= mBorderCornerDimensions[C_BL].height; 1.986 + end.y += mBorderCornerDimensions[C_TL].height; 1.987 + } 1.988 + 1.989 + mContext->NewPath(); 1.990 + mContext->MoveTo(start); 1.991 + mContext->LineTo(end); 1.992 + mContext->SetLineWidth(borderWidth); 1.993 + mContext->SetColor(gfxRGBA(borderColor)); 1.994 + //mContext->SetColor(gfxRGBA(1.0, 0.0, 0.0, 1.0)); 1.995 + mContext->Stroke(); 1.996 +} 1.997 + 1.998 +void 1.999 +nsCSSBorderRenderer::SetupStrokeStyle(mozilla::css::Side aSide) 1.1000 +{ 1.1001 + mContext->SetColor(gfxRGBA(mBorderColors[aSide])); 1.1002 + mContext->SetLineWidth(mBorderWidths[aSide]); 1.1003 +} 1.1004 + 1.1005 +bool 1.1006 +nsCSSBorderRenderer::AllBordersSameWidth() 1.1007 +{ 1.1008 + if (mBorderWidths[0] == mBorderWidths[1] && 1.1009 + mBorderWidths[0] == mBorderWidths[2] && 1.1010 + mBorderWidths[0] == mBorderWidths[3]) 1.1011 + { 1.1012 + return true; 1.1013 + } 1.1014 + 1.1015 + return false; 1.1016 +} 1.1017 + 1.1018 +bool 1.1019 +nsCSSBorderRenderer::AllBordersSolid(bool *aHasCompositeColors) 1.1020 +{ 1.1021 + *aHasCompositeColors = false; 1.1022 + NS_FOR_CSS_SIDES(i) { 1.1023 + if (mCompositeColors[i] != nullptr) { 1.1024 + *aHasCompositeColors = true; 1.1025 + } 1.1026 + if (mBorderStyles[i] == NS_STYLE_BORDER_STYLE_SOLID || 1.1027 + mBorderStyles[i] == NS_STYLE_BORDER_STYLE_NONE || 1.1028 + mBorderStyles[i] == NS_STYLE_BORDER_STYLE_HIDDEN) 1.1029 + { 1.1030 + continue; 1.1031 + } 1.1032 + return false; 1.1033 + } 1.1034 + 1.1035 + return true; 1.1036 +} 1.1037 + 1.1038 +bool IsVisible(int aStyle) 1.1039 +{ 1.1040 + if (aStyle != NS_STYLE_BORDER_STYLE_NONE && 1.1041 + aStyle != NS_STYLE_BORDER_STYLE_HIDDEN) { 1.1042 + return true; 1.1043 + } 1.1044 + return false; 1.1045 +} 1.1046 + 1.1047 +already_AddRefed<gfxPattern> 1.1048 +nsCSSBorderRenderer::CreateCornerGradient(mozilla::css::Corner aCorner, 1.1049 + const gfxRGBA &aFirstColor, 1.1050 + const gfxRGBA &aSecondColor) 1.1051 +{ 1.1052 + typedef struct { gfxFloat a, b; } twoFloats; 1.1053 + 1.1054 + const twoFloats gradientCoeff[4] = { { -1, +1 }, 1.1055 + { -1, -1 }, 1.1056 + { +1, -1 }, 1.1057 + { +1, +1 } }; 1.1058 + 1.1059 + // Sides which form the 'width' and 'height' for the calculation of the angle 1.1060 + // for our gradient. 1.1061 + const int cornerWidth[4] = { 3, 1, 1, 3 }; 1.1062 + const int cornerHeight[4] = { 0, 0, 2, 2 }; 1.1063 + 1.1064 + gfxPoint cornerOrigin = mOuterRect.AtCorner(aCorner); 1.1065 + 1.1066 + gfxPoint pat1, pat2; 1.1067 + pat1.x = cornerOrigin.x + 1.1068 + mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a; 1.1069 + pat1.y = cornerOrigin.y + 1.1070 + mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b; 1.1071 + pat2.x = cornerOrigin.x - 1.1072 + mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a; 1.1073 + pat2.y = cornerOrigin.y - 1.1074 + mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b; 1.1075 + 1.1076 + float gradientOffset; 1.1077 + 1.1078 + if (mContext->IsCairo() && 1.1079 + (mContext->OriginalSurface()->GetType() == gfxSurfaceType::D2D || 1.1080 + mContext->OriginalSurface()->GetType() == gfxSurfaceType::Quartz)) 1.1081 + { 1.1082 + // On quarz this doesn't do exactly the right thing, but it does do what 1.1083 + // most other browsers do and doing the 'right' thing seems to be 1.1084 + // hard with the quartz cairo backend. 1.1085 + gradientOffset = 0; 1.1086 + } else { 1.1087 + // When cairo/Azure does the gradient drawing this gives us pretty nice behavior! 1.1088 + gradientOffset = 0.25 / sqrt(pow(mBorderWidths[cornerHeight[aCorner]], 2) + 1.1089 + pow(mBorderWidths[cornerHeight[aCorner]], 2)); 1.1090 + } 1.1091 + 1.1092 + nsRefPtr<gfxPattern> pattern = new gfxPattern(pat1.x, pat1.y, pat2.x, pat2.y); 1.1093 + pattern->AddColorStop(0.5 - gradientOffset, gfxRGBA(aFirstColor)); 1.1094 + pattern->AddColorStop(0.5 + gradientOffset, gfxRGBA(aSecondColor)); 1.1095 + 1.1096 + return pattern.forget(); 1.1097 +} 1.1098 + 1.1099 +TemporaryRef<GradientStops> 1.1100 +nsCSSBorderRenderer::CreateCornerGradient(mozilla::css::Corner aCorner, 1.1101 + const gfxRGBA &aFirstColor, 1.1102 + const gfxRGBA &aSecondColor, 1.1103 + DrawTarget *aDT, 1.1104 + Point &aPoint1, 1.1105 + Point &aPoint2) 1.1106 +{ 1.1107 + typedef struct { gfxFloat a, b; } twoFloats; 1.1108 + 1.1109 + const twoFloats gradientCoeff[4] = { { -1, +1 }, 1.1110 + { -1, -1 }, 1.1111 + { +1, -1 }, 1.1112 + { +1, +1 } }; 1.1113 + 1.1114 + // Sides which form the 'width' and 'height' for the calculation of the angle 1.1115 + // for our gradient. 1.1116 + const int cornerWidth[4] = { 3, 1, 1, 3 }; 1.1117 + const int cornerHeight[4] = { 0, 0, 2, 2 }; 1.1118 + 1.1119 + gfxPoint cornerOrigin = mOuterRect.AtCorner(aCorner); 1.1120 + 1.1121 + gfxPoint pat1, pat2; 1.1122 + pat1.x = cornerOrigin.x + 1.1123 + mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a; 1.1124 + pat1.y = cornerOrigin.y + 1.1125 + mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b; 1.1126 + pat2.x = cornerOrigin.x - 1.1127 + mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a; 1.1128 + pat2.y = cornerOrigin.y - 1.1129 + mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b; 1.1130 + 1.1131 + aPoint1 = Point(pat1.x, pat1.y); 1.1132 + aPoint2 = Point(pat2.x, pat2.y); 1.1133 + 1.1134 + Color firstColor = ToColor(aFirstColor); 1.1135 + Color secondColor = ToColor(aSecondColor); 1.1136 + 1.1137 + nsTArray<gfx::GradientStop> rawStops(2); 1.1138 + rawStops.SetLength(2); 1.1139 + // This is only guaranteed to give correct (and in some cases more correct) 1.1140 + // rendering with the Direct2D Azure and Quartz Cairo backends. For other 1.1141 + // cairo backends it could create un-antialiased border corner transitions 1.1142 + // since that at least used to be pixman's behaviour for hard stops. 1.1143 + rawStops[0].color = firstColor; 1.1144 + rawStops[0].offset = 0.5; 1.1145 + rawStops[1].color = secondColor; 1.1146 + rawStops[1].offset = 0.5; 1.1147 + RefPtr<GradientStops> gs = 1.1148 + gfxGradientCache::GetGradientStops(aDT, rawStops, ExtendMode::CLAMP); 1.1149 + if (!gs) { 1.1150 + // Having two corners, both with reversed color stops is pretty common 1.1151 + // for certain border types. Let's optimize it! 1.1152 + rawStops[0].color = secondColor; 1.1153 + rawStops[1].color = firstColor; 1.1154 + Point tmp = aPoint1; 1.1155 + aPoint1 = aPoint2; 1.1156 + aPoint2 = tmp; 1.1157 + gs = gfxGradientCache::GetOrCreateGradientStops(aDT, rawStops, ExtendMode::CLAMP); 1.1158 + } 1.1159 + return gs; 1.1160 +} 1.1161 + 1.1162 +typedef struct { gfxFloat a, b; } twoFloats; 1.1163 + 1.1164 +void 1.1165 +nsCSSBorderRenderer::DrawSingleWidthSolidBorder() 1.1166 +{ 1.1167 + // Easy enough to deal with. 1.1168 + mContext->SetLineWidth(1); 1.1169 + gfxRect rect = mOuterRect; 1.1170 + rect.Deflate(0.5); 1.1171 + 1.1172 + const twoFloats cornerAdjusts[4] = { { +0.5, 0 }, 1.1173 + { 0, +0.5 }, 1.1174 + { -0.5, 0 }, 1.1175 + { 0, -0.5 } }; 1.1176 + 1.1177 + 1.1178 + NS_FOR_CSS_SIDES(side) { 1.1179 + gfxPoint firstCorner = rect.CCWCorner(side); 1.1180 + firstCorner.x += cornerAdjusts[side].a; 1.1181 + firstCorner.y += cornerAdjusts[side].b; 1.1182 + gfxPoint secondCorner = rect.CWCorner(side); 1.1183 + secondCorner.x += cornerAdjusts[side].a; 1.1184 + secondCorner.y += cornerAdjusts[side].b; 1.1185 + 1.1186 + mContext->SetColor(gfxRGBA(mBorderColors[side])); 1.1187 + mContext->NewPath(); 1.1188 + mContext->MoveTo(firstCorner); 1.1189 + mContext->LineTo(secondCorner); 1.1190 + mContext->Stroke(); 1.1191 + } 1.1192 +} 1.1193 + 1.1194 +void 1.1195 +nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder() 1.1196 +{ 1.1197 + const gfxFloat alpha = 0.55191497064665766025; 1.1198 + 1.1199 + const twoFloats cornerMults[4] = { { -1, 0 }, 1.1200 + { 0, -1 }, 1.1201 + { +1, 0 }, 1.1202 + { 0, +1 } }; 1.1203 + 1.1204 + const twoFloats centerAdjusts[4] = { { 0, +0.5 }, 1.1205 + { -0.5, 0 }, 1.1206 + { 0, -0.5 }, 1.1207 + { +0.5, 0 } }; 1.1208 + 1.1209 + gfxPoint pc, pci, p0, p1, p2, p3, pd, p3i; 1.1210 + 1.1211 + gfxCornerSizes innerRadii; 1.1212 + ComputeInnerRadii(mBorderRadii, mBorderWidths, &innerRadii); 1.1213 + 1.1214 + gfxRect strokeRect = mOuterRect; 1.1215 + strokeRect.Deflate(gfxMargin(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0, 1.1216 + mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0)); 1.1217 + 1.1218 + NS_FOR_CSS_CORNERS(i) { 1.1219 + // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) 1.1220 + mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4); 1.1221 + mozilla::css::Corner prevCorner = mozilla::css::Corner(i); 1.1222 + 1.1223 + // i+2 and i+3 respectively. These are used to index into the corner 1.1224 + // multiplier table, and were deduced by calculating out the long form 1.1225 + // of each corner and finding a pattern in the signs and values. 1.1226 + int i1 = (i+1) % 4; 1.1227 + int i2 = (i+2) % 4; 1.1228 + int i3 = (i+3) % 4; 1.1229 + 1.1230 + pc = mOuterRect.AtCorner(c); 1.1231 + pci = mInnerRect.AtCorner(c); 1.1232 + mContext->SetLineWidth(mBorderWidths[i]); 1.1233 + 1.1234 + nscolor firstColor, secondColor; 1.1235 + if (IsVisible(mBorderStyles[i]) && IsVisible(mBorderStyles[i1])) { 1.1236 + firstColor = mBorderColors[i]; 1.1237 + secondColor = mBorderColors[i1]; 1.1238 + } else if (IsVisible(mBorderStyles[i])) { 1.1239 + firstColor = mBorderColors[i]; 1.1240 + secondColor = mBorderColors[i]; 1.1241 + } else { 1.1242 + firstColor = mBorderColors[i1]; 1.1243 + secondColor = mBorderColors[i1]; 1.1244 + } 1.1245 + 1.1246 + mContext->NewPath(); 1.1247 + 1.1248 + gfxPoint strokeStart, strokeEnd; 1.1249 + 1.1250 + strokeStart.x = mOuterRect.AtCorner(prevCorner).x + 1.1251 + mBorderCornerDimensions[prevCorner].width * cornerMults[i2].a; 1.1252 + strokeStart.y = mOuterRect.AtCorner(prevCorner).y + 1.1253 + mBorderCornerDimensions[prevCorner].height * cornerMults[i2].b; 1.1254 + 1.1255 + strokeEnd.x = pc.x + mBorderCornerDimensions[c].width * cornerMults[i].a; 1.1256 + strokeEnd.y = pc.y + mBorderCornerDimensions[c].height * cornerMults[i].b; 1.1257 + 1.1258 + strokeStart.x += centerAdjusts[i].a * mBorderWidths[i]; 1.1259 + strokeStart.y += centerAdjusts[i].b * mBorderWidths[i]; 1.1260 + strokeEnd.x += centerAdjusts[i].a * mBorderWidths[i]; 1.1261 + strokeEnd.y += centerAdjusts[i].b * mBorderWidths[i]; 1.1262 + 1.1263 + mContext->MoveTo(strokeStart); 1.1264 + mContext->LineTo(strokeEnd); 1.1265 + mContext->SetColor(gfxRGBA(mBorderColors[i])); 1.1266 + mContext->Stroke(); 1.1267 + 1.1268 + if (firstColor != secondColor) { 1.1269 + nsRefPtr<gfxPattern> pattern = 1.1270 + CreateCornerGradient(c, firstColor, secondColor); 1.1271 + mContext->SetPattern(pattern); 1.1272 + } else { 1.1273 + mContext->SetColor(firstColor); 1.1274 + } 1.1275 + 1.1276 + if (mBorderRadii[c].width > 0 && mBorderRadii[c].height > 0) { 1.1277 + p0.x = pc.x + cornerMults[i].a * mBorderRadii[c].width; 1.1278 + p0.y = pc.y + cornerMults[i].b * mBorderRadii[c].height; 1.1279 + 1.1280 + p3.x = pc.x + cornerMults[i3].a * mBorderRadii[c].width; 1.1281 + p3.y = pc.y + cornerMults[i3].b * mBorderRadii[c].height; 1.1282 + 1.1283 + p1.x = p0.x + alpha * cornerMults[i2].a * mBorderRadii[c].width; 1.1284 + p1.y = p0.y + alpha * cornerMults[i2].b * mBorderRadii[c].height; 1.1285 + 1.1286 + p2.x = p3.x - alpha * cornerMults[i3].a * mBorderRadii[c].width; 1.1287 + p2.y = p3.y - alpha * cornerMults[i3].b * mBorderRadii[c].height; 1.1288 + 1.1289 + mContext->NewPath(); 1.1290 + 1.1291 + gfxPoint cornerStart; 1.1292 + cornerStart.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width; 1.1293 + cornerStart.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height; 1.1294 + 1.1295 + mContext->MoveTo(cornerStart); 1.1296 + mContext->LineTo(p0); 1.1297 + 1.1298 + mContext->CurveTo(p1, p2, p3); 1.1299 + 1.1300 + gfxPoint outerCornerEnd; 1.1301 + outerCornerEnd.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width; 1.1302 + outerCornerEnd.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height; 1.1303 + 1.1304 + mContext->LineTo(outerCornerEnd); 1.1305 + 1.1306 + p0.x = pci.x + cornerMults[i].a * innerRadii[c].width; 1.1307 + p0.y = pci.y + cornerMults[i].b * innerRadii[c].height; 1.1308 + 1.1309 + p3i.x = pci.x + cornerMults[i3].a * innerRadii[c].width; 1.1310 + p3i.y = pci.y + cornerMults[i3].b * innerRadii[c].height; 1.1311 + 1.1312 + p1.x = p0.x + alpha * cornerMults[i2].a * innerRadii[c].width; 1.1313 + p1.y = p0.y + alpha * cornerMults[i2].b * innerRadii[c].height; 1.1314 + 1.1315 + p2.x = p3i.x - alpha * cornerMults[i3].a * innerRadii[c].width; 1.1316 + p2.y = p3i.y - alpha * cornerMults[i3].b * innerRadii[c].height; 1.1317 + mContext->LineTo(p3i); 1.1318 + mContext->CurveTo(p2, p1, p0); 1.1319 + mContext->ClosePath(); 1.1320 + mContext->Fill(); 1.1321 + } else { 1.1322 + gfxPoint c1, c2, c3, c4; 1.1323 + 1.1324 + c1.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width; 1.1325 + c1.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height; 1.1326 + c2 = pc; 1.1327 + c3.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width; 1.1328 + c3.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height; 1.1329 + 1.1330 + mContext->NewPath(); 1.1331 + mContext->MoveTo(c1); 1.1332 + mContext->LineTo(c2); 1.1333 + mContext->LineTo(c3); 1.1334 + mContext->LineTo(pci); 1.1335 + mContext->ClosePath(); 1.1336 + 1.1337 + mContext->Fill(); 1.1338 + } 1.1339 + } 1.1340 +} 1.1341 + 1.1342 +void 1.1343 +nsCSSBorderRenderer::DrawNoCompositeColorSolidBorderAzure() 1.1344 +{ 1.1345 + DrawTarget *dt = mContext->GetDrawTarget(); 1.1346 + 1.1347 + const gfxFloat alpha = 0.55191497064665766025; 1.1348 + 1.1349 + const twoFloats cornerMults[4] = { { -1, 0 }, 1.1350 + { 0, -1 }, 1.1351 + { +1, 0 }, 1.1352 + { 0, +1 } }; 1.1353 + 1.1354 + const twoFloats centerAdjusts[4] = { { 0, +0.5 }, 1.1355 + { -0.5, 0 }, 1.1356 + { 0, -0.5 }, 1.1357 + { +0.5, 0 } }; 1.1358 + 1.1359 + Point pc, pci, p0, p1, p2, p3, pd, p3i; 1.1360 + 1.1361 + gfxCornerSizes innerRadii; 1.1362 + ComputeInnerRadii(mBorderRadii, mBorderWidths, &innerRadii); 1.1363 + 1.1364 + gfxRect strokeRect = mOuterRect; 1.1365 + strokeRect.Deflate(gfxMargin(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0, 1.1366 + mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0)); 1.1367 + 1.1368 + ColorPattern colorPat(Color(0, 0, 0, 0)); 1.1369 + LinearGradientPattern gradPat(Point(), Point(), nullptr); 1.1370 + 1.1371 + NS_FOR_CSS_CORNERS(i) { 1.1372 + // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) 1.1373 + mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4); 1.1374 + mozilla::css::Corner prevCorner = mozilla::css::Corner(i); 1.1375 + 1.1376 + // i+2 and i+3 respectively. These are used to index into the corner 1.1377 + // multiplier table, and were deduced by calculating out the long form 1.1378 + // of each corner and finding a pattern in the signs and values. 1.1379 + int i1 = (i+1) % 4; 1.1380 + int i2 = (i+2) % 4; 1.1381 + int i3 = (i+3) % 4; 1.1382 + 1.1383 + pc = ToPoint(mOuterRect.AtCorner(c)); 1.1384 + pci = ToPoint(mInnerRect.AtCorner(c)); 1.1385 + 1.1386 + nscolor firstColor, secondColor; 1.1387 + if (IsVisible(mBorderStyles[i]) && IsVisible(mBorderStyles[i1])) { 1.1388 + firstColor = mBorderColors[i]; 1.1389 + secondColor = mBorderColors[i1]; 1.1390 + } else if (IsVisible(mBorderStyles[i])) { 1.1391 + firstColor = mBorderColors[i]; 1.1392 + secondColor = mBorderColors[i]; 1.1393 + } else { 1.1394 + firstColor = mBorderColors[i1]; 1.1395 + secondColor = mBorderColors[i1]; 1.1396 + } 1.1397 + 1.1398 + RefPtr<PathBuilder> builder = dt->CreatePathBuilder(); 1.1399 + 1.1400 + Point strokeStart, strokeEnd; 1.1401 + 1.1402 + strokeStart.x = mOuterRect.AtCorner(prevCorner).x + 1.1403 + mBorderCornerDimensions[prevCorner].width * cornerMults[i2].a; 1.1404 + strokeStart.y = mOuterRect.AtCorner(prevCorner).y + 1.1405 + mBorderCornerDimensions[prevCorner].height * cornerMults[i2].b; 1.1406 + 1.1407 + strokeEnd.x = pc.x + mBorderCornerDimensions[c].width * cornerMults[i].a; 1.1408 + strokeEnd.y = pc.y + mBorderCornerDimensions[c].height * cornerMults[i].b; 1.1409 + 1.1410 + strokeStart.x += centerAdjusts[i].a * mBorderWidths[i]; 1.1411 + strokeStart.y += centerAdjusts[i].b * mBorderWidths[i]; 1.1412 + strokeEnd.x += centerAdjusts[i].a * mBorderWidths[i]; 1.1413 + strokeEnd.y += centerAdjusts[i].b * mBorderWidths[i]; 1.1414 + 1.1415 + builder->MoveTo(strokeStart); 1.1416 + builder->LineTo(strokeEnd); 1.1417 + RefPtr<Path> path = builder->Finish(); 1.1418 + dt->Stroke(path, ColorPattern(Color::FromABGR(mBorderColors[i])), StrokeOptions(mBorderWidths[i])); 1.1419 + builder = nullptr; 1.1420 + path = nullptr; 1.1421 + 1.1422 + Pattern *pattern; 1.1423 + 1.1424 + if (firstColor != secondColor) { 1.1425 + gradPat.mStops = CreateCornerGradient(c, firstColor, secondColor, dt, gradPat.mBegin, gradPat.mEnd); 1.1426 + pattern = &gradPat; 1.1427 + } else { 1.1428 + colorPat.mColor = Color::FromABGR(firstColor); 1.1429 + pattern = &colorPat; 1.1430 + } 1.1431 + 1.1432 + builder = dt->CreatePathBuilder(); 1.1433 + 1.1434 + if (mBorderRadii[c].width > 0 && mBorderRadii[c].height > 0) { 1.1435 + p0.x = pc.x + cornerMults[i].a * mBorderRadii[c].width; 1.1436 + p0.y = pc.y + cornerMults[i].b * mBorderRadii[c].height; 1.1437 + 1.1438 + p3.x = pc.x + cornerMults[i3].a * mBorderRadii[c].width; 1.1439 + p3.y = pc.y + cornerMults[i3].b * mBorderRadii[c].height; 1.1440 + 1.1441 + p1.x = p0.x + alpha * cornerMults[i2].a * mBorderRadii[c].width; 1.1442 + p1.y = p0.y + alpha * cornerMults[i2].b * mBorderRadii[c].height; 1.1443 + 1.1444 + p2.x = p3.x - alpha * cornerMults[i3].a * mBorderRadii[c].width; 1.1445 + p2.y = p3.y - alpha * cornerMults[i3].b * mBorderRadii[c].height; 1.1446 + 1.1447 + Point cornerStart; 1.1448 + cornerStart.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width; 1.1449 + cornerStart.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height; 1.1450 + 1.1451 + builder->MoveTo(cornerStart); 1.1452 + builder->LineTo(p0); 1.1453 + 1.1454 + builder->BezierTo(p1, p2, p3); 1.1455 + 1.1456 + Point outerCornerEnd; 1.1457 + outerCornerEnd.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width; 1.1458 + outerCornerEnd.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height; 1.1459 + 1.1460 + builder->LineTo(outerCornerEnd); 1.1461 + 1.1462 + p0.x = pci.x + cornerMults[i].a * innerRadii[c].width; 1.1463 + p0.y = pci.y + cornerMults[i].b * innerRadii[c].height; 1.1464 + 1.1465 + p3i.x = pci.x + cornerMults[i3].a * innerRadii[c].width; 1.1466 + p3i.y = pci.y + cornerMults[i3].b * innerRadii[c].height; 1.1467 + 1.1468 + p1.x = p0.x + alpha * cornerMults[i2].a * innerRadii[c].width; 1.1469 + p1.y = p0.y + alpha * cornerMults[i2].b * innerRadii[c].height; 1.1470 + 1.1471 + p2.x = p3i.x - alpha * cornerMults[i3].a * innerRadii[c].width; 1.1472 + p2.y = p3i.y - alpha * cornerMults[i3].b * innerRadii[c].height; 1.1473 + builder->LineTo(p3i); 1.1474 + builder->BezierTo(p2, p1, p0); 1.1475 + builder->Close(); 1.1476 + path = builder->Finish(); 1.1477 + dt->Fill(path, *pattern); 1.1478 + } else { 1.1479 + Point c1, c2, c3, c4; 1.1480 + 1.1481 + c1.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width; 1.1482 + c1.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height; 1.1483 + c2 = pc; 1.1484 + c3.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width; 1.1485 + c3.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height; 1.1486 + 1.1487 + builder->MoveTo(c1); 1.1488 + builder->LineTo(c2); 1.1489 + builder->LineTo(c3); 1.1490 + builder->LineTo(pci); 1.1491 + builder->Close(); 1.1492 + 1.1493 + path = builder->Finish(); 1.1494 + 1.1495 + dt->Fill(path, *pattern); 1.1496 + } 1.1497 + } 1.1498 +} 1.1499 + 1.1500 +void 1.1501 +nsCSSBorderRenderer::DrawRectangularCompositeColors() 1.1502 +{ 1.1503 + nsBorderColors *currentColors[4]; 1.1504 + mContext->SetLineWidth(1); 1.1505 + memcpy(currentColors, mCompositeColors, sizeof(nsBorderColors*) * 4); 1.1506 + gfxRect rect = mOuterRect; 1.1507 + rect.Deflate(0.5); 1.1508 + 1.1509 + const twoFloats cornerAdjusts[4] = { { +0.5, 0 }, 1.1510 + { 0, +0.5 }, 1.1511 + { -0.5, 0 }, 1.1512 + { 0, -0.5 } }; 1.1513 + 1.1514 + for (int i = 0; i < mBorderWidths[0]; i++) { 1.1515 + NS_FOR_CSS_SIDES(side) { 1.1516 + int sideNext = (side + 1) % 4; 1.1517 + 1.1518 + gfxPoint firstCorner = rect.CCWCorner(side); 1.1519 + firstCorner.x += cornerAdjusts[side].a; 1.1520 + firstCorner.y += cornerAdjusts[side].b; 1.1521 + gfxPoint secondCorner = rect.CWCorner(side); 1.1522 + secondCorner.x -= cornerAdjusts[side].a; 1.1523 + secondCorner.y -= cornerAdjusts[side].b; 1.1524 + 1.1525 + gfxRGBA currentColor = 1.1526 + currentColors[side] ? gfxRGBA(currentColors[side]->mColor) 1.1527 + : gfxRGBA(mBorderColors[side]); 1.1528 + 1.1529 + mContext->SetColor(currentColor); 1.1530 + mContext->NewPath(); 1.1531 + mContext->MoveTo(firstCorner); 1.1532 + mContext->LineTo(secondCorner); 1.1533 + mContext->Stroke(); 1.1534 + 1.1535 + mContext->NewPath(); 1.1536 + gfxPoint cornerTopLeft = rect.CWCorner(side); 1.1537 + cornerTopLeft.x -= 0.5; 1.1538 + cornerTopLeft.y -= 0.5; 1.1539 + mContext->Rectangle(gfxRect(cornerTopLeft, gfxSize(1, 1))); 1.1540 + gfxRGBA nextColor = 1.1541 + currentColors[sideNext] ? gfxRGBA(currentColors[sideNext]->mColor) 1.1542 + : gfxRGBA(mBorderColors[sideNext]); 1.1543 + 1.1544 + gfxRGBA cornerColor((currentColor.r + nextColor.r) / 2.0, 1.1545 + (currentColor.g + nextColor.g) / 2.0, 1.1546 + (currentColor.b + nextColor.b) / 2.0, 1.1547 + (currentColor.a + nextColor.a) / 2.0); 1.1548 + mContext->SetColor(cornerColor); 1.1549 + mContext->Fill(); 1.1550 + 1.1551 + if (side != 0) { 1.1552 + // We'll have to keep side 0 for the color averaging on side 3. 1.1553 + if (currentColors[side] && currentColors[side]->mNext) { 1.1554 + currentColors[side] = currentColors[side]->mNext; 1.1555 + } 1.1556 + } 1.1557 + } 1.1558 + // Now advance the color for side 0. 1.1559 + if (currentColors[0] && currentColors[0]->mNext) { 1.1560 + currentColors[0] = currentColors[0]->mNext; 1.1561 + } 1.1562 + rect.Deflate(1); 1.1563 + } 1.1564 +} 1.1565 + 1.1566 +void 1.1567 +nsCSSBorderRenderer::DrawBorders() 1.1568 +{ 1.1569 + bool forceSeparateCorners = false; 1.1570 + 1.1571 + // Examine the border style to figure out if we can draw it in one 1.1572 + // go or not. 1.1573 + bool tlBordersSame = AreBorderSideFinalStylesSame(SIDE_BIT_TOP | SIDE_BIT_LEFT); 1.1574 + bool brBordersSame = AreBorderSideFinalStylesSame(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT); 1.1575 + bool allBordersSame = AreBorderSideFinalStylesSame(SIDE_BITS_ALL); 1.1576 + if (allBordersSame && 1.1577 + ((mCompositeColors[0] == nullptr && 1.1578 + (mBorderStyles[0] == NS_STYLE_BORDER_STYLE_NONE || 1.1579 + mBorderStyles[0] == NS_STYLE_BORDER_STYLE_HIDDEN || 1.1580 + mBorderColors[0] == NS_RGBA(0,0,0,0))) || 1.1581 + (mCompositeColors[0] && 1.1582 + (mCompositeColors[0]->mColor == NS_RGBA(0,0,0,0) && 1.1583 + !mCompositeColors[0]->mNext)))) 1.1584 + { 1.1585 + // All borders are the same style, and the style is either none or hidden, or the color 1.1586 + // is transparent. 1.1587 + // This also checks if the first composite color is transparent, and there are 1.1588 + // no others. It doesn't check if there are subsequent transparent ones, because 1.1589 + // that would be very silly. 1.1590 + return; 1.1591 + } 1.1592 + 1.1593 + gfxMatrix mat = mContext->CurrentMatrix(); 1.1594 + 1.1595 + // Clamp the CTM to be pixel-aligned; we do this only 1.1596 + // for translation-only matrices now, but we could do it 1.1597 + // if the matrix has just a scale as well. We should not 1.1598 + // do it if there's a rotation. 1.1599 + if (mat.HasNonTranslation()) { 1.1600 + if (!mat.HasNonAxisAlignedTransform()) { 1.1601 + // Scale + transform. Avoid stroke fast-paths so that we have a chance 1.1602 + // of snapping to pixel boundaries. 1.1603 + mAvoidStroke = true; 1.1604 + } 1.1605 + } else { 1.1606 + mat.x0 = floor(mat.x0 + 0.5); 1.1607 + mat.y0 = floor(mat.y0 + 0.5); 1.1608 + mContext->SetMatrix(mat); 1.1609 + 1.1610 + // round mOuterRect and mInnerRect; they're already an integer 1.1611 + // number of pixels apart and should stay that way after 1.1612 + // rounding. We don't do this if there's a scale in the current transform 1.1613 + // since this loses information that might be relevant when we're scaling. 1.1614 + mOuterRect.Round(); 1.1615 + mInnerRect.Round(); 1.1616 + } 1.1617 + 1.1618 + bool allBordersSameWidth = AllBordersSameWidth(); 1.1619 + 1.1620 + if (allBordersSameWidth && mBorderWidths[0] == 0.0) { 1.1621 + // Some of the allBordersSameWidth codepaths depend on the border 1.1622 + // width being greater than zero. 1.1623 + return; 1.1624 + } 1.1625 + 1.1626 + bool allBordersSolid; 1.1627 + 1.1628 + // First there's a couple of 'special cases' that have specifically optimized 1.1629 + // drawing paths, when none of these can be used we move on to the generalized 1.1630 + // border drawing code. 1.1631 + if (allBordersSame && 1.1632 + mCompositeColors[0] == nullptr && 1.1633 + allBordersSameWidth && 1.1634 + mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID && 1.1635 + mNoBorderRadius && 1.1636 + !mAvoidStroke) 1.1637 + { 1.1638 + // Very simple case. 1.1639 + SetupStrokeStyle(NS_SIDE_TOP); 1.1640 + gfxRect rect = mOuterRect; 1.1641 + rect.Deflate(mBorderWidths[0] / 2.0); 1.1642 + mContext->NewPath(); 1.1643 + mContext->Rectangle(rect); 1.1644 + mContext->Stroke(); 1.1645 + return; 1.1646 + } 1.1647 + 1.1648 + if (allBordersSame && 1.1649 + mCompositeColors[0] == nullptr && 1.1650 + allBordersSameWidth && 1.1651 + mBorderStyles[0] == NS_STYLE_BORDER_STYLE_DOTTED && 1.1652 + mBorderWidths[0] < 3 && 1.1653 + mNoBorderRadius && 1.1654 + !mAvoidStroke) 1.1655 + { 1.1656 + // Very simple case. We draw this rectangular dotted borner without 1.1657 + // antialiasing. The dots should be pixel aligned. 1.1658 + SetupStrokeStyle(NS_SIDE_TOP); 1.1659 + 1.1660 + gfxFloat dash = mBorderWidths[0]; 1.1661 + mContext->SetDash(&dash, 1, 0.5); 1.1662 + mContext->SetAntialiasMode(gfxContext::MODE_ALIASED); 1.1663 + gfxRect rect = mOuterRect; 1.1664 + rect.Deflate(mBorderWidths[0] / 2.0); 1.1665 + mContext->NewPath(); 1.1666 + mContext->Rectangle(rect); 1.1667 + mContext->Stroke(); 1.1668 + return; 1.1669 + } 1.1670 + 1.1671 + 1.1672 + if (allBordersSame && 1.1673 + mCompositeColors[0] == nullptr && 1.1674 + mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID && 1.1675 + !mAvoidStroke && 1.1676 + !mNoBorderRadius) 1.1677 + { 1.1678 + // Relatively simple case. 1.1679 + SetupStrokeStyle(NS_SIDE_TOP); 1.1680 + 1.1681 + RoundedRect borderInnerRect(mOuterRect, mBorderRadii); 1.1682 + borderInnerRect.Deflate(mBorderWidths[NS_SIDE_TOP], 1.1683 + mBorderWidths[NS_SIDE_BOTTOM], 1.1684 + mBorderWidths[NS_SIDE_LEFT], 1.1685 + mBorderWidths[NS_SIDE_RIGHT]); 1.1686 + 1.1687 + // Instead of stroking we just use two paths: an inner and an outer. 1.1688 + // This allows us to draw borders that we couldn't when stroking. For example, 1.1689 + // borders with a border width >= the border radius. (i.e. when there are 1.1690 + // square corners on the inside) 1.1691 + // 1.1692 + // Further, this approach can be more efficient because the backend 1.1693 + // doesn't need to compute an offset curve to stroke the path. We know that 1.1694 + // the rounded parts are elipses we can offset exactly and can just compute 1.1695 + // a new cubic approximation. 1.1696 + mContext->NewPath(); 1.1697 + mContext->RoundedRectangle(mOuterRect, mBorderRadii, true); 1.1698 + mContext->RoundedRectangle(borderInnerRect.rect, borderInnerRect.corners, false); 1.1699 + mContext->Fill(); 1.1700 + return; 1.1701 + } 1.1702 + 1.1703 + bool hasCompositeColors; 1.1704 + 1.1705 + allBordersSolid = AllBordersSolid(&hasCompositeColors); 1.1706 + // This leaves the border corners non-interpolated for single width borders. 1.1707 + // Doing this is slightly faster and shouldn't be a problem visually. 1.1708 + if (allBordersSolid && 1.1709 + allBordersSameWidth && 1.1710 + mCompositeColors[0] == nullptr && 1.1711 + mBorderWidths[0] == 1 && 1.1712 + mNoBorderRadius && 1.1713 + !mAvoidStroke) 1.1714 + { 1.1715 + DrawSingleWidthSolidBorder(); 1.1716 + return; 1.1717 + } 1.1718 + 1.1719 + if (allBordersSolid && !hasCompositeColors && 1.1720 + !mAvoidStroke) 1.1721 + { 1.1722 + if (mContext->IsCairo()) { 1.1723 + DrawNoCompositeColorSolidBorder(); 1.1724 + } else { 1.1725 + DrawNoCompositeColorSolidBorderAzure(); 1.1726 + } 1.1727 + return; 1.1728 + } 1.1729 + 1.1730 + if (allBordersSolid && 1.1731 + allBordersSameWidth && 1.1732 + mNoBorderRadius && 1.1733 + !mAvoidStroke) 1.1734 + { 1.1735 + // Easy enough to deal with. 1.1736 + DrawRectangularCompositeColors(); 1.1737 + return; 1.1738 + } 1.1739 + 1.1740 + // If we have composite colors -and- border radius, 1.1741 + // then use separate corners so we get OPERATOR_ADD for the corners. 1.1742 + // Otherwise, we'll get artifacts as we draw stacked 1px-wide curves. 1.1743 + if (allBordersSame && mCompositeColors[0] != nullptr && !mNoBorderRadius) 1.1744 + forceSeparateCorners = true; 1.1745 + 1.1746 + S(" mOuterRect: "), S(mOuterRect), SN(); 1.1747 + S(" mInnerRect: "), S(mInnerRect), SN(); 1.1748 + SF(" mBorderColors: 0x%08x 0x%08x 0x%08x 0x%08x\n", mBorderColors[0], mBorderColors[1], mBorderColors[2], mBorderColors[3]); 1.1749 + 1.1750 + // if conditioning the outside rect failed, then bail -- the outside 1.1751 + // rect is supposed to enclose the entire border 1.1752 + mOuterRect.Condition(); 1.1753 + if (mOuterRect.IsEmpty()) 1.1754 + return; 1.1755 + 1.1756 + mInnerRect.Condition(); 1.1757 + int dashedSides = 0; 1.1758 + 1.1759 + NS_FOR_CSS_SIDES(i) { 1.1760 + uint8_t style = mBorderStyles[i]; 1.1761 + if (style == NS_STYLE_BORDER_STYLE_DASHED || 1.1762 + style == NS_STYLE_BORDER_STYLE_DOTTED) 1.1763 + { 1.1764 + // pretend that all borders aren't the same; we need to draw 1.1765 + // things separately for dashed/dotting 1.1766 + allBordersSame = false; 1.1767 + dashedSides |= (1 << i); 1.1768 + } 1.1769 + } 1.1770 + 1.1771 + SF(" allBordersSame: %d dashedSides: 0x%02x\n", allBordersSame, dashedSides); 1.1772 + 1.1773 + if (allBordersSame && !forceSeparateCorners) { 1.1774 + /* Draw everything in one go */ 1.1775 + DrawBorderSides(SIDE_BITS_ALL); 1.1776 + SN("---------------- (1)"); 1.1777 + } else { 1.1778 + PROFILER_LABEL("nsCSSBorderRenderer", "DrawBorders::multipass"); 1.1779 + /* We have more than one pass to go. Draw the corners separately from the sides. */ 1.1780 + 1.1781 + /* 1.1782 + * If we have a 1px-wide border, the corners are going to be 1.1783 + * negligible, so don't bother doing anything fancy. Just extend 1.1784 + * the top and bottom borders to the right 1px and the left border 1.1785 + * to the bottom 1px. We do this by twiddling the corner dimensions, 1.1786 + * which causes the right to happen later on. Only do this if we have 1.1787 + * a 1.0 unit border all around and no border radius. 1.1788 + */ 1.1789 + 1.1790 + NS_FOR_CSS_CORNERS(corner) { 1.1791 + const mozilla::css::Side sides[2] = { mozilla::css::Side(corner), PREV_SIDE(corner) }; 1.1792 + 1.1793 + if (!IsZeroSize(mBorderRadii[corner])) 1.1794 + continue; 1.1795 + 1.1796 + if (mBorderWidths[sides[0]] == 1.0 && mBorderWidths[sides[1]] == 1.0) { 1.1797 + if (corner == NS_CORNER_TOP_LEFT || corner == NS_CORNER_TOP_RIGHT) 1.1798 + mBorderCornerDimensions[corner].width = 0.0; 1.1799 + else 1.1800 + mBorderCornerDimensions[corner].height = 0.0; 1.1801 + } 1.1802 + } 1.1803 + 1.1804 + // First, the corners 1.1805 + NS_FOR_CSS_CORNERS(corner) { 1.1806 + // if there's no corner, don't do all this work for it 1.1807 + if (IsZeroSize(mBorderCornerDimensions[corner])) 1.1808 + continue; 1.1809 + 1.1810 + const int sides[2] = { corner, PREV_SIDE(corner) }; 1.1811 + int sideBits = (1 << sides[0]) | (1 << sides[1]); 1.1812 + 1.1813 + bool simpleCornerStyle = mCompositeColors[sides[0]] == nullptr && 1.1814 + mCompositeColors[sides[1]] == nullptr && 1.1815 + AreBorderSideFinalStylesSame(sideBits); 1.1816 + 1.1817 + // If we don't have anything complex going on in this corner, 1.1818 + // then we can just fill the corner with a solid color, and avoid 1.1819 + // the potentially expensive clip. 1.1820 + if (simpleCornerStyle && 1.1821 + IsZeroSize(mBorderRadii[corner]) && 1.1822 + IsSolidCornerStyle(mBorderStyles[sides[0]], corner)) 1.1823 + { 1.1824 + mContext->NewPath(); 1.1825 + DoCornerSubPath(corner); 1.1826 + mContext->SetColor(MakeBorderColor(mBorderColors[sides[0]], 1.1827 + mBackgroundColor, 1.1828 + BorderColorStyleForSolidCorner(mBorderStyles[sides[0]], corner))); 1.1829 + mContext->Fill(); 1.1830 + continue; 1.1831 + } 1.1832 + 1.1833 + mContext->Save(); 1.1834 + 1.1835 + // clip to the corner 1.1836 + mContext->NewPath(); 1.1837 + DoCornerSubPath(corner); 1.1838 + mContext->Clip(); 1.1839 + 1.1840 + if (simpleCornerStyle) { 1.1841 + // we don't need a group for this corner, the sides are the same, 1.1842 + // but we weren't able to render just a solid block for the corner. 1.1843 + DrawBorderSides(sideBits); 1.1844 + } else { 1.1845 + // Sides are different. We could draw using OPERATOR_ADD to 1.1846 + // get correct color blending behaviour at the seam. We'd need 1.1847 + // to do it in an offscreen surface to ensure that we're 1.1848 + // always compositing on transparent black. If the colors 1.1849 + // don't have transparency and the current destination surface 1.1850 + // has an alpha channel, we could just clear the region and 1.1851 + // avoid the temporary, but that situation doesn't happen all 1.1852 + // that often in practice (we double buffer to no-alpha 1.1853 + // surfaces). We choose just to seam though, as the performance 1.1854 + // advantages outway the modest easthetic improvement. 1.1855 + 1.1856 + for (int cornerSide = 0; cornerSide < 2; cornerSide++) { 1.1857 + mozilla::css::Side side = mozilla::css::Side(sides[cornerSide]); 1.1858 + uint8_t style = mBorderStyles[side]; 1.1859 + 1.1860 + SF("corner: %d cornerSide: %d side: %d style: %d\n", corner, cornerSide, side, style); 1.1861 + 1.1862 + mContext->Save(); 1.1863 + 1.1864 + mContext->NewPath(); 1.1865 + DoSideClipSubPath(side); 1.1866 + mContext->Clip(); 1.1867 + 1.1868 + DrawBorderSides(1 << side); 1.1869 + 1.1870 + mContext->Restore(); 1.1871 + } 1.1872 + } 1.1873 + 1.1874 + mContext->Restore(); 1.1875 + 1.1876 + SN(); 1.1877 + } 1.1878 + 1.1879 + // in the case of a single-unit border, we already munged the 1.1880 + // corners up above; so we can just draw the top left and bottom 1.1881 + // right sides separately, if they're the same. 1.1882 + // 1.1883 + // We need to check for mNoBorderRadius, because when there is 1.1884 + // one, FillSolidBorder always draws the full rounded rectangle 1.1885 + // and expects there to be a clip in place. 1.1886 + int alreadyDrawnSides = 0; 1.1887 + if (mOneUnitBorder && 1.1888 + mNoBorderRadius && 1.1889 + (dashedSides & (SIDE_BIT_TOP | SIDE_BIT_LEFT)) == 0) 1.1890 + { 1.1891 + if (tlBordersSame) { 1.1892 + DrawBorderSides(SIDE_BIT_TOP | SIDE_BIT_LEFT); 1.1893 + alreadyDrawnSides |= (SIDE_BIT_TOP | SIDE_BIT_LEFT); 1.1894 + } 1.1895 + 1.1896 + if (brBordersSame && (dashedSides & (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT)) == 0) { 1.1897 + DrawBorderSides(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT); 1.1898 + alreadyDrawnSides |= (SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT); 1.1899 + } 1.1900 + } 1.1901 + 1.1902 + // We're done with the corners, now draw the sides. 1.1903 + NS_FOR_CSS_SIDES (side) { 1.1904 + // if we drew it above, skip it 1.1905 + if (alreadyDrawnSides & (1 << side)) 1.1906 + continue; 1.1907 + 1.1908 + // If there's no border on this side, skip it 1.1909 + if (mBorderWidths[side] == 0.0 || 1.1910 + mBorderStyles[side] == NS_STYLE_BORDER_STYLE_HIDDEN || 1.1911 + mBorderStyles[side] == NS_STYLE_BORDER_STYLE_NONE) 1.1912 + continue; 1.1913 + 1.1914 + 1.1915 + if (dashedSides & (1 << side)) { 1.1916 + // Dashed sides will always draw just the part ignoring the 1.1917 + // corners for the side, so no need to clip. 1.1918 + DrawDashedSide (side); 1.1919 + 1.1920 + SN("---------------- (d)"); 1.1921 + continue; 1.1922 + } 1.1923 + 1.1924 + // Undashed sides will currently draw the entire side, 1.1925 + // including parts that would normally be covered by a corner, 1.1926 + // so we need to clip. 1.1927 + // 1.1928 + // XXX Optimization -- it would be good to make this work like 1.1929 + // DrawDashedSide, and have a DrawOneSide function that just 1.1930 + // draws one side and not the corners, because then we can 1.1931 + // avoid the potentially expensive clip. 1.1932 + mContext->Save(); 1.1933 + mContext->NewPath(); 1.1934 + DoSideClipWithoutCornersSubPath(side); 1.1935 + mContext->Clip(); 1.1936 + 1.1937 + DrawBorderSides(1 << side); 1.1938 + 1.1939 + mContext->Restore(); 1.1940 + 1.1941 + SN("---------------- (*)"); 1.1942 + } 1.1943 + } 1.1944 +}