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