LCOV - code coverage report
Current view: top level - layout/painting - DashedCornerFinder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 169 0.6 %
Date: 2017-07-14 16:53:18 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "DashedCornerFinder.h"
       7             : 
       8             : #include "mozilla/Move.h"
       9             : #include "BorderCache.h"
      10             : #include "BorderConsts.h"
      11             : 
      12             : namespace mozilla {
      13             : 
      14             : using namespace gfx;
      15             : 
      16             : struct BestDashLength
      17             : {
      18             :   typedef mozilla::gfx::Float Float;
      19             : 
      20             :   Float dashLength;
      21             :   size_t count;
      22             : 
      23           0 :   BestDashLength()
      24           0 :    : dashLength(0.0f), count(0)
      25           0 :   {}
      26             : 
      27           0 :   BestDashLength(Float aDashLength, size_t aCount)
      28           0 :    : dashLength(aDashLength), count(aCount)
      29           0 :   {}
      30             : };
      31             : 
      32             : static const size_t DashedCornerCacheSize = 256;
      33           3 : nsDataHashtable<FourFloatsHashKey, BestDashLength> DashedCornerCache;
      34             : 
      35           0 : DashedCornerFinder::DashedCornerFinder(const Bezier& aOuterBezier,
      36             :                                        const Bezier& aInnerBezier,
      37             :                                        Float aBorderWidthH, Float aBorderWidthV,
      38           0 :                                        const Size& aCornerDim)
      39             :  : mOuterBezier(aOuterBezier),
      40             :    mInnerBezier(aInnerBezier),
      41             :    mLastOuterP(aOuterBezier.mPoints[0]), mLastInnerP(aInnerBezier.mPoints[0]),
      42             :    mLastOuterT(0.0f), mLastInnerT(0.0f),
      43             :    mBestDashLength(DOT_LENGTH * DASH_LENGTH),
      44             :    mHasZeroBorderWidth(false), mHasMore(true),
      45           0 :    mMaxCount(aCornerDim.width + aCornerDim.height),
      46             :    mType(OTHER),
      47           0 :    mI(0), mCount(0)
      48             : {
      49           0 :   NS_ASSERTION(aBorderWidthH > 0.0f || aBorderWidthV > 0.0f,
      50             :                "At least one side should have non-zero width.");
      51             : 
      52           0 :   DetermineType(aBorderWidthH, aBorderWidthV);
      53             : 
      54           0 :   Reset();
      55           0 : }
      56             : 
      57             : void
      58           0 : DashedCornerFinder::DetermineType(Float aBorderWidthH, Float aBorderWidthV)
      59             : {
      60           0 :   if (aBorderWidthH < aBorderWidthV) {
      61             :     // Always draw from wider side to thinner side.
      62           0 :     Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
      63           0 :     Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
      64           0 :     Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
      65           0 :     Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
      66           0 :     mLastOuterP = mOuterBezier.mPoints[0];
      67           0 :     mLastInnerP = mInnerBezier.mPoints[0];
      68             :   }
      69             : 
      70             :   // See the comment at mType declaration for each condition.
      71             : 
      72           0 :   Float borderRadiusA = fabs(mOuterBezier.mPoints[0].x -
      73           0 :                              mOuterBezier.mPoints[3].x);
      74           0 :   Float borderRadiusB = fabs(mOuterBezier.mPoints[0].y -
      75           0 :                              mOuterBezier.mPoints[3].y);
      76           0 :   if (aBorderWidthH == aBorderWidthV &&
      77           0 :       borderRadiusA == borderRadiusB &&
      78           0 :       borderRadiusA > aBorderWidthH * 2.0f) {
      79           0 :     Float curveHeight = borderRadiusA - aBorderWidthH / 2.0;
      80             : 
      81           0 :     mType = PERFECT;
      82           0 :     Float borderLength = M_PI * curveHeight / 2.0f;
      83             : 
      84           0 :     Float dashWidth = aBorderWidthH * DOT_LENGTH * DASH_LENGTH;
      85           0 :     size_t count = ceil(borderLength / dashWidth);
      86           0 :     if (count % 2) {
      87           0 :       count++;
      88             :     }
      89           0 :     mCount = count / 2 + 1;
      90           0 :     mBestDashLength = borderLength / (aBorderWidthH * count);
      91             :   }
      92             : 
      93           0 :   Float minBorderWidth = std::min(aBorderWidthH, aBorderWidthV);
      94           0 :   if (minBorderWidth == 0.0f) {
      95           0 :     mHasZeroBorderWidth = true;
      96             :   }
      97             : 
      98           0 :   if (mType == OTHER && !mHasZeroBorderWidth) {
      99           0 :     Float minBorderRadius = std::min(borderRadiusA, borderRadiusB);
     100           0 :     Float maxBorderRadius = std::max(borderRadiusA, borderRadiusB);
     101           0 :     Float maxBorderWidth = std::max(aBorderWidthH, aBorderWidthV);
     102             : 
     103             :     FindBestDashLength(minBorderWidth, maxBorderWidth,
     104           0 :                        minBorderRadius, maxBorderRadius);
     105             :   }
     106           0 : }
     107             : 
     108             : bool
     109           0 : DashedCornerFinder::HasMore(void) const
     110             : {
     111           0 :   if (mHasZeroBorderWidth) {
     112           0 :     return mI < mMaxCount && mHasMore;
     113             :   }
     114             : 
     115           0 :   return mI < mCount;
     116             : }
     117             : 
     118             : DashedCornerFinder::Result
     119           0 : DashedCornerFinder::Next(void)
     120             : {
     121             :   Float lastOuterT, lastInnerT, outerT, innerT;
     122             : 
     123           0 :   if (mI == 0) {
     124           0 :     lastOuterT = 0.0f;
     125           0 :     lastInnerT = 0.0f;
     126             :   } else {
     127           0 :     if (mType == PERFECT) {
     128           0 :       lastOuterT = lastInnerT = (mI * 2.0f - 0.5f) / ((mCount - 1) * 2.0f);
     129             :     } else {
     130           0 :       Float last2OuterT = mLastOuterT;
     131           0 :       Float last2InnerT = mLastInnerT;
     132             : 
     133           0 :       (void)FindNext(mBestDashLength);
     134             : 
     135             :       //
     136             :       //          mLastOuterT   lastOuterT
     137             :       //                    |   |
     138             :       //                    v   v
     139             :       //            +---+---+---+---+ <- last2OuterT
     140             :       //            |   |###|###|   |
     141             :       //            |   |###|###|   |
     142             :       //            |   |###|###|   |
     143             :       //            +---+---+---+---+ <- last2InnerT
     144             :       //                    ^   ^
     145             :       //                    |   |
     146             :       //          mLastInnerT   lastInnerT
     147           0 :       lastOuterT = (mLastOuterT + last2OuterT) / 2.0f;
     148           0 :       lastInnerT = (mLastInnerT + last2InnerT) / 2.0f;
     149             :     }
     150             :   }
     151             : 
     152           0 :   if ((!mHasZeroBorderWidth && mI == mCount - 1) ||
     153           0 :       (mHasZeroBorderWidth && !mHasMore)) {
     154           0 :     outerT = 1.0f;
     155           0 :     innerT = 1.0f;
     156             :   } else {
     157           0 :     if (mType == PERFECT) {
     158           0 :       outerT = innerT = (mI * 2.0f + 0.5f) / ((mCount - 1) * 2.0f);
     159             :     } else {
     160           0 :       Float last2OuterT = mLastOuterT;
     161           0 :       Float last2InnerT = mLastInnerT;
     162             : 
     163           0 :       (void)FindNext(mBestDashLength);
     164             : 
     165             :       //
     166             :       //               outerT   last2OuterT
     167             :       //                    |   |
     168             :       //                    v   v
     169             :       // mLastOuterT -> +---+---+---+---+
     170             :       //                |   |###|###|   |
     171             :       //                |   |###|###|   |
     172             :       //                |   |###|###|   |
     173             :       // mLastInnerT -> +---+---+---+---+
     174             :       //                    ^   ^
     175             :       //                    |   |
     176             :       //               innerT   last2InnerT
     177           0 :       outerT = (mLastOuterT + last2OuterT) / 2.0f;
     178           0 :       innerT = (mLastInnerT + last2InnerT) / 2.0f;
     179             :     }
     180             :   }
     181             : 
     182           0 :   mI++;
     183             : 
     184           0 :   Bezier outerSectionBezier;
     185           0 :   Bezier innerSectionBezier;
     186           0 :   GetSubBezier(&outerSectionBezier, mOuterBezier, lastOuterT, outerT);
     187           0 :   GetSubBezier(&innerSectionBezier, mInnerBezier, lastInnerT, innerT);
     188           0 :   return DashedCornerFinder::Result(outerSectionBezier, innerSectionBezier);
     189             : }
     190             : 
     191             : void
     192           0 : DashedCornerFinder::Reset(void)
     193             : {
     194           0 :   mLastOuterP = mOuterBezier.mPoints[0];
     195           0 :   mLastInnerP = mInnerBezier.mPoints[0];
     196           0 :   mLastOuterT = 0.0f;
     197           0 :   mLastInnerT = 0.0f;
     198           0 :   mHasMore = true;
     199           0 : }
     200             : 
     201             : Float
     202           0 : DashedCornerFinder::FindNext(Float dashLength)
     203             : {
     204           0 :   Float upper = 1.0f;
     205           0 :   Float lower = mLastOuterT;
     206             : 
     207           0 :   Point OuterP, InnerP;
     208             :   // Start from upper bound to check if this is the last segment.
     209           0 :   Float outerT = upper;
     210             :   Float innerT;
     211           0 :   Float W = 0.0f;
     212           0 :   Float L = 0.0f;
     213             : 
     214           0 :   const Float LENGTH_MARGIN = 0.1f;
     215           0 :   for (size_t i = 0; i < MAX_LOOP; i++) {
     216           0 :     OuterP = GetBezierPoint(mOuterBezier, outerT);
     217           0 :     InnerP = FindBezierNearestPoint(mInnerBezier, OuterP, outerT, &innerT);
     218             : 
     219             :     // Calculate approximate dash length.
     220             :     //
     221             :     //   W = (W1 + W2) / 2
     222             :     //   L = (OuterL + InnerL) / 2
     223             :     //   dashLength = L / W
     224             :     //
     225             :     //              ____----+----____
     226             :     // OuterP ___---        |         ---___    mLastOuterP
     227             :     //    +---              |               ---+
     228             :     //    |                  |                 |
     229             :     //    |                  |                 |
     230             :     //     |               W |              W1 |
     231             :     //     |                  |                |
     232             :     //  W2 |                  |                |
     233             :     //     |                  |    ______------+
     234             :     //     |              ____+----             mLastInnerP
     235             :     //      |       ___---
     236             :     //      |  __---
     237             :     //      +--
     238             :     //    InnerP
     239             :     //                     OuterL
     240             :     //              ____---------____
     241             :     // OuterP ___---                  ---___    mLastOuterP
     242             :     //    +---                              ---+
     243             :     //    |                  L                 |
     244             :     //    |            ___----------______     |
     245             :     //     |      __---                   -----+
     246             :     //     |  __--                             |
     247             :     //     +--                                 |
     248             :     //     |                InnerL ______------+
     249             :     //     |              ____-----             mLastInnerP
     250             :     //      |       ___---
     251             :     //      |  __---
     252             :     //      +--
     253             :     //    InnerP
     254           0 :     Float W1 = (mLastOuterP - mLastInnerP).Length();
     255           0 :     Float W2 = (OuterP - InnerP).Length();
     256           0 :     Float OuterL = GetBezierLength(mOuterBezier, mLastOuterT, outerT);
     257           0 :     Float InnerL = GetBezierLength(mInnerBezier, mLastInnerT, innerT);
     258           0 :     W = (W1 + W2) / 2.0f;
     259           0 :     L = (OuterL + InnerL) / 2.0f;
     260           0 :     if (L > W * dashLength + LENGTH_MARGIN) {
     261           0 :       if (i > 0) {
     262           0 :         upper = outerT;
     263             :       }
     264           0 :     } else if (L < W * dashLength - LENGTH_MARGIN) {
     265           0 :       if (i == 0) {
     266             :         // This is the last segment with shorter dashLength.
     267           0 :         mHasMore = false;
     268           0 :         break;
     269             :       }
     270           0 :       lower = outerT;
     271             :     } else {
     272           0 :       break;
     273             :     }
     274             : 
     275           0 :     outerT = (upper + lower) / 2.0f;
     276             :   }
     277             : 
     278           0 :   mLastOuterP = OuterP;
     279           0 :   mLastInnerP = InnerP;
     280           0 :   mLastOuterT = outerT;
     281           0 :   mLastInnerT = innerT;
     282             : 
     283           0 :   if (W == 0.0f) {
     284           0 :     return 1.0f;
     285             :   }
     286             : 
     287           0 :   return L / W;
     288             : }
     289             : 
     290             : void
     291           0 : DashedCornerFinder::FindBestDashLength(Float aMinBorderWidth,
     292             :                                        Float aMaxBorderWidth,
     293             :                                        Float aMinBorderRadius,
     294             :                                        Float aMaxBorderRadius)
     295             : {
     296             :   // If dashLength is not calculateable, find it with binary search,
     297             :   // such that there exists i that OuterP_i == OuterP_n and
     298             :   // InnerP_i == InnerP_n with given dashLength.
     299             : 
     300             :   FourFloats key(aMinBorderWidth, aMaxBorderWidth,
     301           0 :                  aMinBorderRadius, aMaxBorderRadius);
     302           0 :   BestDashLength best;
     303           0 :   if (DashedCornerCache.Get(key, &best)) {
     304           0 :     mCount = best.count;
     305           0 :     mBestDashLength = best.dashLength;
     306           0 :     return;
     307             :   }
     308             : 
     309           0 :   Float lower = 1.0f;
     310           0 :   Float upper = DOT_LENGTH * DASH_LENGTH;
     311           0 :   Float dashLength = upper;
     312           0 :   size_t targetCount = 0;
     313             : 
     314           0 :   const Float LENGTH_MARGIN = 0.1f;
     315           0 :   for (size_t j = 0; j < MAX_LOOP; j++) {
     316             :     size_t count;
     317             :     Float actualDashLength;
     318           0 :     if (!GetCountAndLastDashLength(dashLength, &count, &actualDashLength)) {
     319           0 :       if (j == 0) {
     320           0 :         mCount = mMaxCount;
     321           0 :         break;
     322             :       }
     323             :     }
     324             : 
     325           0 :     if (j == 0) {
     326           0 :       if (count == 1) {
     327             :         // If only 1 segment fits, fill entire region
     328             :         //
     329             :         //   count = 1
     330             :         //   mCount = 1
     331             :         //   |   1   |
     332             :         //   +---+---+
     333             :         //   |###|###|
     334             :         //   |###|###|
     335             :         //   |###|###|
     336             :         //   +---+---+
     337             :         //       1
     338           0 :         mCount = 1;
     339           0 :         break;
     340             :       }
     341             : 
     342             :       // targetCount should be 2n.
     343             :       //
     344             :       //   targetCount = 2
     345             :       //   mCount = 2
     346             :       //   |   1   |   2   |
     347             :       //   +---+---+---+---+
     348             :       //   |###|   |   |###|
     349             :       //   |###|   |   |###|
     350             :       //   |###|   |   |###|
     351             :       //   +---+---+---+---+
     352             :       //     1           2
     353             :       //
     354             :       //   targetCount = 6
     355             :       //   mCount = 4
     356             :       //   |   1   |   2   |   3   |   4   |   5   |   6   |
     357             :       //   +---+---+---+---+---+---+---+---+---+---+---+---+
     358             :       //   |###|   |   |###|###|   |   |###|###|   |   |###|
     359             :       //   |###|   |   |###|###|   |   |###|###|   |   |###|
     360             :       //   |###|   |   |###|###|   |   |###|###|   |   |###|
     361             :       //   +---+---+---+---+---+---+---+---+---+---+---+---+
     362             :       //     1             2               3             4
     363           0 :       if (count % 2) {
     364           0 :         targetCount = count + 1;
     365             :       } else {
     366           0 :         targetCount = count;
     367             :       }
     368             : 
     369           0 :       mCount = targetCount / 2 + 1;
     370             :     }
     371             : 
     372           0 :     if (count == targetCount) {
     373           0 :       mBestDashLength = dashLength;
     374             : 
     375             :       // actualDashLength won't be greater than dashLength.
     376           0 :       if (actualDashLength > dashLength - LENGTH_MARGIN) {
     377           0 :         break;
     378             :       }
     379             : 
     380             :       // We started from upper bound, no need to update range when j == 0.
     381           0 :       if (j > 0) {
     382           0 :         upper = dashLength;
     383             :       }
     384             :     } else {
     385             :       // |j == 0 && count != targetCount| means that |targetCount = count + 1|,
     386             :       // and we started from upper bound, no need to update range when j == 0.
     387           0 :       if (j > 0) {
     388           0 :         if (count > targetCount) {
     389           0 :           lower = dashLength;
     390             :         } else {
     391           0 :           upper = dashLength;
     392             :         }
     393             :       }
     394             :     }
     395             : 
     396           0 :     dashLength = (upper + lower) / 2.0f;
     397             :   }
     398             : 
     399           0 :   if (DashedCornerCache.Count() > DashedCornerCacheSize) {
     400           0 :     DashedCornerCache.Clear();
     401             :   }
     402           0 :   DashedCornerCache.Put(key, BestDashLength(mBestDashLength, mCount));
     403             : }
     404             : 
     405             : bool
     406           0 : DashedCornerFinder::GetCountAndLastDashLength(Float aDashLength,
     407             :                                               size_t* aCount,
     408             :                                               Float* aActualDashLength)
     409             : {
     410             :   // Return the number of segments and the last segment's dashLength for
     411             :   // the given dashLength.
     412             : 
     413           0 :   Reset();
     414             : 
     415           0 :   for (size_t i = 0; i < mMaxCount; i++) {
     416           0 :     Float actualDashLength = FindNext(aDashLength);
     417           0 :     if (mLastOuterT >= 1.0f) {
     418           0 :       *aCount = i + 1;
     419           0 :       *aActualDashLength = actualDashLength;
     420           0 :       return true;
     421             :     }
     422             :   }
     423             : 
     424           0 :   return false;
     425             : }
     426             : 
     427             : } // namespace mozilla

Generated by: LCOV version 1.13