LCOV - code coverage report
Current view: directory - layout/base - nsCSSRenderingBorders.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 783 0 0.0 %
Date: 2012-06-02 Functions: 29 0 0.0 %

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

Generated by: LCOV version 1.7