layout/base/nsCSSRenderingBorders.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial