--- a/gfx/2d/BasePoint.h
+++ b/gfx/2d/BasePoint.h
@@ -77,16 +77,20 @@ struct BasePoint {
T DotProduct(const Sub& aPoint) const {
return x * aPoint.x + y * aPoint.y;
}
T Length() const {
return hypot(x, y);
}
+ T LengthSquare() const {
+ return x * x + y * y;
+ }
+
// Round() is *not* rounding to nearest integer if the values are negative.
// They are always rounding as floor(n + 0.5).
// See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
Sub& Round() {
x = Coord(floor(T(x) + T(0.5)));
y = Coord(floor(T(y) + T(0.5)));
return *static_cast<Sub*>(this);
}
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BezierUtils.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BezierUtils.h"
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+Point
+GetBezierPoint(const Bezier& aBezier, Float t)
+{
+ Float s = 1.0f - t;
+
+ return Point(
+ aBezier.mPoints[0].x * s * s * s +
+ 3.0f * aBezier.mPoints[1].x * t * s * s +
+ 3.0f * aBezier.mPoints[2].x * t * t * s +
+ aBezier.mPoints[3].x * t * t * t,
+ aBezier.mPoints[0].y * s * s * s +
+ 3.0f * aBezier.mPoints[1].y * t * s * s +
+ 3.0f * aBezier.mPoints[2].y * t * t * s +
+ aBezier.mPoints[3].y * t * t * t
+ );
+}
+
+Point
+GetBezierDifferential(const Bezier& aBezier, Float t)
+{
+ // Return P'(t).
+
+ Float s = 1.0f - t;
+
+ return Point(
+ -3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
+ 2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
+ (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
+ -3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
+ 2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
+ (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
+ );
+}
+
+Point
+GetBezierDifferential2(const Bezier& aBezier, Float t)
+{
+ // Return P''(t).
+
+ Float s = 1.0f - t;
+
+ return Point(
+ 6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
+ (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
+ (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
+ 6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
+ (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
+ (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
+ );
+}
+
+Float
+GetBezierLength(const Bezier& aBezier, Float a, Float b)
+{
+ if (a < 0.5f && b > 0.5f) {
+ // To increase the accuracy, split into two parts.
+ return GetBezierLength(aBezier, a, 0.5f) +
+ GetBezierLength(aBezier, 0.5f, b);
+ }
+
+ // Calculate length of simple bezier curve with Simpson's rule.
+ // _
+ // / b
+ // length = | |P'(x)| dx
+ // _/ a
+ //
+ // b - a a + b
+ // = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
+ // 6 2
+
+ Float fa = GetBezierDifferential(aBezier, a).Length();
+ Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
+ Float fb = GetBezierDifferential(aBezier, b).Length();
+
+ return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
+}
+
+static void
+SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+ // Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
+
+ Float s = 1.0f - t;
+
+ Point tmp1;
+ Point tmp2;
+
+ aSubBezier->mPoints[0] = aBezier.mPoints[0];
+
+ aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+ tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+ tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+
+ aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
+ tmp1 = tmp1 * s + tmp2 * t;
+
+ aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
+}
+
+static void
+SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+ // Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
+
+ Float s = 1.0f - t;
+
+ Point tmp1;
+ Point tmp2;
+
+ aSubBezier->mPoints[3] = aBezier.mPoints[3];
+
+ aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+ tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+ tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+
+ aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
+ tmp1 = tmp2 * s + tmp1 * t;
+
+ aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
+}
+
+void
+GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
+{
+ Bezier tmp;
+ SplitBezierB(&tmp, aBezier, t1);
+
+ Float range = 1.0f - t1;
+ if (range == 0.0f) {
+ *aSubBezier = tmp;
+ } else {
+ SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
+ }
+}
+
+static Point
+BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float* aT)
+{
+ // Find a nearest point on bezier curve with Binary search.
+ // Called from FindBezierNearestPoint.
+
+ Float lower = 0.0f;
+ Float upper = 1.0f;
+ Float t;
+
+ Point P, lastP;
+ const size_t MAX_LOOP = 32;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+ const Float DIFF = 0.0001f;
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ t = (upper + lower) / 2.0f;
+ P = GetBezierPoint(aBezier, t);
+
+ // Check if it converged.
+ if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
+ break;
+ }
+
+ Float distSquare = (P - aTarget).LengthSquare();
+ if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
+ distSquare) {
+ lower = t;
+ } else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
+ distSquare) {
+ upper = t;
+ } else {
+ break;
+ }
+
+ lastP = P;
+ }
+
+ if (aT) {
+ *aT = t;
+ }
+
+ return P;
+}
+
+Point
+FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float aInitialT, Float* aT)
+{
+ // Find a nearest point on bezier curve with Newton's method.
+ // It converges within 4 iterations in most cases.
+ //
+ // f(t_n)
+ // t_{n+1} = t_n - ---------
+ // f'(t_n)
+ //
+ // d 2
+ // f(t) = ---- | P(t) - aTarget |
+ // dt
+
+ Float t = aInitialT;
+ Point P;
+ Point lastP = GetBezierPoint(aBezier, t);
+
+ const size_t MAX_LOOP = 4;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+ for (size_t i = 0; i <= MAX_LOOP; i++) {
+ Point dP = GetBezierDifferential(aBezier, t);
+ Point ddP = GetBezierDifferential2(aBezier, t);
+ Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
+ Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
+ aTarget.DotProduct(ddP));
+ t = t - f / df;
+ P = GetBezierPoint(aBezier, t);
+ if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
+ break;
+ }
+ lastP = P;
+
+ if (i == MAX_LOOP) {
+ // If aInitialT is too bad, it won't converge in a few iterations,
+ // fallback to binary search.
+ return BisectBezierNearestPoint(aBezier, aTarget, aT);
+ }
+ }
+
+ if (aT) {
+ *aT = t;
+ }
+
+ return P;
+}
+
+void
+GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+ const Point& aCornerPoint, const Size& aCornerSize)
+{
+ // Calculate bezier control points for elliptic arc.
+
+ const Float signsList[4][2] = {
+ { +1.0f, +1.0f },
+ { -1.0f, +1.0f },
+ { -1.0f, -1.0f },
+ { +1.0f, -1.0f }
+ };
+ const Float (& signs)[2] = signsList[aCorner];
+
+ aBezier->mPoints[0] = aCornerPoint;
+ aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
+
+ aBezier->mPoints[1] = aBezier->mPoints[0];
+ aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
+
+ aBezier->mPoints[3] = aCornerPoint;
+ aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
+
+ aBezier->mPoints[2] = aBezier->mPoints[3];
+ aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
+}
+
+Float
+GetQuarterEllipticArcLength(Float a, Float b)
+{
+ // Calculate the approximate length of a quarter elliptic arc formed by radii
+ // (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
+ // _ _
+ // | 2 |
+ // | 3 * (a - b) |
+ // p = PI | (a + b) + ------------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * (a + b) + sqrt(a + 14 * a * b + b ) _|
+ //
+ // _ _
+ // | 2 |
+ // | 3 * (a - b) |
+ // = PI | (a + b) + -------------------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * (a + b) + sqrt(4 * (a + b) - 3 * (a - b) ) _|
+ //
+ // _ _
+ // | 2 |
+ // | 3 * S |
+ // = PI | A + -------------------------------------- |
+ // | 2 2 |
+ // |_ 10 * A + sqrt(4 * A - 3 * S ) _|
+ //
+ // where A = a + b, S = a - b
+
+ Float A = a + b, S = a - b;
+ Float A2 = A * A, S2 = S * S;
+ Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
+ return p / 4.0f;
+}
+
+Float
+CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+ const Point& origin, Float width, Float height)
+{
+ // Solve following equations with n and return smaller n.
+ //
+ // / (x, y) = P + n * normal
+ // |
+ // < _ _ 2 _ _ 2
+ // | | x - origin.x | | y - origin.y |
+ // | | ------------ | + | ------------ | = 1
+ // \ |_ width _| |_ height _|
+
+ Float a = (P.x - origin.x) / width;
+ Float b = normal.x / width;
+ Float c = (P.y - origin.y) / height;
+ Float d = normal.y / height;
+
+ Float A = b * b + d * d;
+ Float B = a * b + c * d;
+ Float C = a * a + c * c - 1;
+
+ Float S = sqrt(B * B - A * C);
+
+ Float n1 = (- B + S) / A;
+ Float n2 = (- B - S) / A;
+
+ return n1 < n2 ? n1 : n2;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BezierUtils.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BezierUtils_h_
+#define mozilla_BezierUtils_h_
+
+#include "mozilla/gfx/2D.h"
+#include "gfxRect.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Control points for bezier curve
+//
+// mPoints[2]
+// +-----___---+ mPoints[3]
+// __--
+// _--
+// /
+// /
+// mPoints[1] + |
+// | |
+// ||
+// ||
+// |
+// |
+// |
+// |
+// mPoints[0] +
+struct Bezier {
+ Point mPoints[4];
+};
+
+// Calculate a point or it's differential of a bezier curve formed by
+// aBezier and parameter t.
+//
+// GetBezierPoint = P(t)
+// GetBezierDifferential = P'(t)
+// GetBezierDifferential2 = P''(t)
+//
+// mPoints[2]
+// +-----___---+ mPoints[3]
+// __-- P(1)
+// _--
+// +
+// / P(t)
+// mPoints[1] + |
+// | |
+// ||
+// ||
+// |
+// |
+// |
+// |
+// mPoints[0] + P(0)
+Point GetBezierPoint(const Bezier& aBezier, Float t);
+Point GetBezierDifferential(const Bezier& aBezier, Float t);
+Point GetBezierDifferential2(const Bezier& aBezier, Float t);
+
+// Calculate length of a simple bezier curve formed by aBezier and range [a, b].
+Float GetBezierLength(const Bezier& aBezier, Float a, Float b);
+
+// Split bezier curve formed by aBezier into [0,t1], [t1,t2], [t2,1] parts, and
+// stores control points for [t1,t2] to aSubBezier.
+//
+// ___---+
+// __+- P(1)
+// _-- P(t2)
+// -
+// / <-- aSubBezier
+// |
+// |
+// +
+// | P(t1)
+// |
+// |
+// |
+// |
+// + P(0)
+void GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier,
+ Float t1, Float t2);
+
+// Find a nearest point on bezier curve formed by aBezier to a point aTarget.
+// aInitialT is a hint to find the parameter t for the nearest point.
+// If aT is non-null, parameter for the nearest point is stored to *aT.
+// This function expects a bezier curve to be an approximation of elliptic arc.
+// Otherwise it will return wrong point.
+//
+// aTarget
+// + ___---+
+// __--
+// _--
+// +
+// / nearest point = P(t = *aT)
+// |
+// |
+// |
+// + P(aInitialT)
+// |
+// |
+// |
+// |
+// +
+Point FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+ Float aInitialT, Float* aT=nullptr);
+
+// Calculate control points for a bezier curve that is an approximation of
+// an elliptic arc.
+//
+// aCornerSize.width
+// |<----------------->|
+// | |
+// aCornerPoint| mPoints[2] |
+// -------------+-------+-----___---+ mPoints[3]
+// ^ | __--
+// | | _--
+// | | -
+// | | /
+// aCornerSize.height | mPoints[1] + |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v mPoints[0] |
+// -------------+
+void GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+ const Point& aCornerPoint,
+ const Size& aCornerSize);
+
+// Calculate the approximate length of a quarter elliptic arc formed by radii
+// (a, b).
+//
+// a
+// |<----------------->|
+// | |
+// ---+-------------___---+
+// ^ | __--
+// | | _--
+// | | -
+// | | /
+// b | | |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v |
+// ---+
+Float GetQuarterEllipticArcLength(Float a, Float b);
+
+// Calculate the distance between an elliptic arc formed by (origin, width,
+// height), and a point P, along a line formed by |P + n * normal|.
+// P should be outside of the ellipse, and the line should cross with the
+// ellipse twice at n > 0 points.
+//
+// width
+// |<----------------->|
+// origin | |
+// -----------+-------------___---+
+// ^ normal | __--
+// | P +->__ | _--
+// | --__ -
+// | | --+
+// height | | |
+// | | |
+// | ||
+// | ||
+// | |
+// | |
+// | |
+// v |
+// -----------+
+Float CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+ const Point& origin,
+ Float width, Float height);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* mozilla_BezierUtils_h_ */
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -12,16 +12,17 @@ EXPORTS.mozilla.gfx += [
'2D.h',
'BaseCoord.h',
'BaseMargin.h',
'BasePoint.h',
'BasePoint3D.h',
'BasePoint4D.h',
'BaseRect.h',
'BaseSize.h',
+ 'BezierUtils.h',
'Blur.h',
'BorrowedContext.h',
'Coord.h',
'CriticalSection.h',
'DataSurfaceHelpers.h',
'DrawEventRecorder.h',
'DrawTargetTiled.h',
'Filters.h',
@@ -133,16 +134,17 @@ elif CONFIG['CPU_ARCH'].startswith('mips
'BlurLS3.cpp',
]
if CONFIG['MOZ_ENABLE_SKIA']:
SOURCES += [
'convolverLS3.cpp',
]
UNIFIED_SOURCES += [
+ 'BezierUtils.cpp',
'Blur.cpp',
'DataSourceSurface.cpp',
'DataSurfaceHelpers.cpp',
'DrawEventRecorder.cpp',
'DrawingJob.cpp',
'DrawTarget.cpp',
'DrawTargetCairo.cpp',
'DrawTargetCapture.cpp',
new file mode 100644
--- /dev/null
+++ b/layout/base/BorderCache.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BorderCache_h_
+#define mozilla_BorderCache_h_
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/HashFunctions.h"
+#include "nsDataHashtable.h"
+#include "PLDHashTable.h"
+
+namespace mozilla {
+// Cache for best overlap and best dashLength.
+
+struct FourFloats
+{
+ typedef mozilla::gfx::Float Float;
+
+ Float n[4];
+
+ FourFloats()
+ {
+ n[0] = 0.0f;
+ n[1] = 0.0f;
+ n[2] = 0.0f;
+ n[3] = 0.0f;
+ }
+
+ FourFloats(Float a, Float b, Float c, Float d)
+ {
+ n[0] = a;
+ n[1] = b;
+ n[2] = c;
+ n[3] = d;
+ }
+
+ bool
+ operator==(const FourFloats& aOther) const
+ {
+ return n[0] == aOther.n[0] &&
+ n[1] == aOther.n[1] &&
+ n[2] == aOther.n[2] &&
+ n[3] == aOther.n[3];
+ }
+};
+
+class FourFloatsHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const FourFloats& KeyType;
+ typedef const FourFloats* KeyTypePointer;
+
+ explicit FourFloatsHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ FourFloatsHashKey(const FourFloatsHashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~FourFloatsHashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return HashBytes(aKey->n, sizeof(mozilla::gfx::Float) * 4);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const FourFloats mValue;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_BorderCache_h_ */
new file mode 100644
--- /dev/null
+++ b/layout/base/DashedCornerFinder.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DashedCornerFinder.h"
+
+#include "mozilla/Move.h"
+#include "BorderCache.h"
+#include "BorderConsts.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+struct BestDashLength
+{
+ typedef mozilla::gfx::Float Float;
+
+ Float dashLength;
+ size_t count;
+
+ BestDashLength()
+ : dashLength(0.0f), count(0)
+ {}
+
+ BestDashLength(Float aDashLength, size_t aCount)
+ : dashLength(aDashLength), count(aCount)
+ {}
+};
+
+static const size_t DashedCornerCacheSize = 256;
+nsDataHashtable<FourFloatsHashKey, BestDashLength> DashedCornerCache;
+
+DashedCornerFinder::DashedCornerFinder(const Bezier& aOuterBezier,
+ const Bezier& aInnerBezier,
+ Float aBorderWidthH, Float aBorderWidthV,
+ const Size& aCornerDim)
+ : mOuterBezier(aOuterBezier),
+ mInnerBezier(aInnerBezier),
+ mLastOuterP(aOuterBezier.mPoints[0]), mLastInnerP(aInnerBezier.mPoints[0]),
+ mLastOuterT(0.0f), mLastInnerT(0.0f),
+ mBestDashLength(DOT_LENGTH * DASH_LENGTH),
+ mHasZeroBorderWidth(false), mHasMore(true),
+ mMaxCount(aCornerDim.width + aCornerDim.height),
+ mType(OTHER),
+ mI(0), mCount(0)
+{
+ NS_ASSERTION(aBorderWidthH > 0.0f || aBorderWidthV > 0.0f,
+ "At least one side should have non-zero width.");
+
+ DetermineType(aBorderWidthH, aBorderWidthV);
+
+ Reset();
+}
+
+void
+DashedCornerFinder::DetermineType(Float aBorderWidthH, Float aBorderWidthV)
+{
+ if (aBorderWidthH < aBorderWidthV) {
+ // Always draw from wider side to thinner side.
+ Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
+ Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
+ Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
+ Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
+ mLastOuterP = mOuterBezier.mPoints[0];
+ mLastInnerP = mInnerBezier.mPoints[0];
+ }
+
+ // See the comment at mType declaration for each condition.
+
+ Float borderRadiusA = fabs(mOuterBezier.mPoints[0].x -
+ mOuterBezier.mPoints[3].x);
+ Float borderRadiusB = fabs(mOuterBezier.mPoints[0].y -
+ mOuterBezier.mPoints[3].y);
+ if (aBorderWidthH == aBorderWidthV &&
+ borderRadiusA == borderRadiusB &&
+ borderRadiusA > aBorderWidthH * 2.0f) {
+ Float curveHeight = borderRadiusA - aBorderWidthH / 2.0;
+
+ mType = PERFECT;
+ Float borderLength = M_PI * curveHeight / 2.0f;
+
+ Float dashWidth = aBorderWidthH * DOT_LENGTH * DASH_LENGTH;
+ size_t count = ceil(borderLength / dashWidth);
+ if (count % 2) {
+ count++;
+ }
+ mCount = count / 2 + 1;
+ mBestDashLength = borderLength / (aBorderWidthH * count);
+ }
+
+ Float minBorderWidth = std::min(aBorderWidthH, aBorderWidthV);
+ if (minBorderWidth == 0.0f) {
+ mHasZeroBorderWidth = true;
+ }
+
+ if (mType == OTHER && !mHasZeroBorderWidth) {
+ Float minBorderRadius = std::min(borderRadiusA, borderRadiusB);
+ Float maxBorderRadius = std::max(borderRadiusA, borderRadiusB);
+ Float maxBorderWidth = std::max(aBorderWidthH, aBorderWidthV);
+
+ FindBestDashLength(minBorderWidth, maxBorderWidth,
+ minBorderRadius, maxBorderRadius);
+ }
+}
+
+bool
+DashedCornerFinder::HasMore(void) const
+{
+ if (mHasZeroBorderWidth) {
+ return mI < mMaxCount && mHasMore;
+ }
+
+ return mI < mCount;
+}
+
+DashedCornerFinder::Result
+DashedCornerFinder::Next(void)
+{
+ Float lastOuterT, lastInnerT, outerT, innerT;
+
+ if (mI == 0) {
+ lastOuterT = 0.0f;
+ lastInnerT = 0.0f;
+ } else {
+ if (mType == PERFECT) {
+ lastOuterT = lastInnerT = (mI * 2.0f - 0.5f) / ((mCount - 1) * 2.0f);
+ } else {
+ Float last2OuterT = mLastOuterT;
+ Float last2InnerT = mLastInnerT;
+
+ (void)FindNext(mBestDashLength);
+
+ //
+ // mLastOuterT lastOuterT
+ // | |
+ // v v
+ // +---+---+---+---+ <- last2OuterT
+ // | |###|###| |
+ // | |###|###| |
+ // | |###|###| |
+ // +---+---+---+---+ <- last2InnerT
+ // ^ ^
+ // | |
+ // mLastInnerT lastInnerT
+ lastOuterT = (mLastOuterT + last2OuterT) / 2.0f;
+ lastInnerT = (mLastInnerT + last2InnerT) / 2.0f;
+ }
+ }
+
+ if ((!mHasZeroBorderWidth && mI == mCount - 1) ||
+ (mHasZeroBorderWidth && !mHasMore)) {
+ outerT = 1.0f;
+ innerT = 1.0f;
+ } else {
+ if (mType == PERFECT) {
+ outerT = innerT = (mI * 2.0f + 0.5f) / ((mCount - 1) * 2.0f);
+ } else {
+ Float last2OuterT = mLastOuterT;
+ Float last2InnerT = mLastInnerT;
+
+ (void)FindNext(mBestDashLength);
+
+ //
+ // outerT last2OuterT
+ // | |
+ // v v
+ // mLastOuterT -> +---+---+---+---+
+ // | |###|###| |
+ // | |###|###| |
+ // | |###|###| |
+ // mLastInnerT -> +---+---+---+---+
+ // ^ ^
+ // | |
+ // innerT last2InnerT
+ outerT = (mLastOuterT + last2OuterT) / 2.0f;
+ innerT = (mLastInnerT + last2InnerT) / 2.0f;
+ }
+ }
+
+ mI++;
+
+ Bezier outerSectionBezier;
+ Bezier innerSectionBezier;
+ GetSubBezier(&outerSectionBezier, mOuterBezier, lastOuterT, outerT);
+ GetSubBezier(&innerSectionBezier, mInnerBezier, lastInnerT, innerT);
+ return DashedCornerFinder::Result(outerSectionBezier, innerSectionBezier);
+}
+
+void
+DashedCornerFinder::Reset(void)
+{
+ mLastOuterP = mOuterBezier.mPoints[0];
+ mLastInnerP = mInnerBezier.mPoints[0];
+ mLastOuterT = 0.0f;
+ mLastInnerT = 0.0f;
+ mHasMore = true;
+}
+
+Float
+DashedCornerFinder::FindNext(Float dashLength)
+{
+ Float upper = 1.0f;
+ Float lower = mLastOuterT;
+
+ Point OuterP, InnerP;
+ // Start from upper bound to check if this is the last segment.
+ Float outerT = upper;
+ Float innerT;
+ Float W = 0.0f;
+ Float L = 0.0f;
+
+ const Float LENGTH_MARGIN = 0.1f;
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ OuterP = GetBezierPoint(mOuterBezier, outerT);
+ InnerP = FindBezierNearestPoint(mInnerBezier, OuterP, outerT, &innerT);
+
+ // Calculate approximate dash length.
+ //
+ // W = (W1 + W2) / 2
+ // L = (OuterL + InnerL) / 2
+ // dashLength = L / W
+ //
+ // ____----+----____
+ // OuterP ___--- | ---___ mLastOuterP
+ // +--- | ---+
+ // | | |
+ // | | |
+ // | W | W1 |
+ // | | |
+ // W2 | | |
+ // | | ______------+
+ // | ____+---- mLastInnerP
+ // | ___---
+ // | __---
+ // +--
+ // InnerP
+ // OuterL
+ // ____---------____
+ // OuterP ___--- ---___ mLastOuterP
+ // +--- ---+
+ // | L |
+ // | ___----------______ |
+ // | __--- -----+
+ // | __-- |
+ // +-- |
+ // | InnerL ______------+
+ // | ____----- mLastInnerP
+ // | ___---
+ // | __---
+ // +--
+ // InnerP
+ Float W1 = (mLastOuterP - mLastInnerP).Length();
+ Float W2 = (OuterP - InnerP).Length();
+ Float OuterL = GetBezierLength(mOuterBezier, mLastOuterT, outerT);
+ Float InnerL = GetBezierLength(mInnerBezier, mLastInnerT, innerT);
+ W = (W1 + W2) / 2.0f;
+ L = (OuterL + InnerL) / 2.0f;
+ if (L > W * dashLength + LENGTH_MARGIN) {
+ if (i > 0) {
+ upper = outerT;
+ }
+ } else if (L < W * dashLength - LENGTH_MARGIN) {
+ if (i == 0) {
+ // This is the last segment with shorter dashLength.
+ mHasMore = false;
+ break;
+ }
+ lower = outerT;
+ } else {
+ break;
+ }
+
+ outerT = (upper + lower) / 2.0f;
+ }
+
+ mLastOuterP = OuterP;
+ mLastInnerP = InnerP;
+ mLastOuterT = outerT;
+ mLastInnerT = innerT;
+
+ if (W == 0.0f) {
+ return 1.0f;
+ }
+
+ return L / W;
+}
+
+void
+DashedCornerFinder::FindBestDashLength(Float aMinBorderWidth,
+ Float aMaxBorderWidth,
+ Float aMinBorderRadius,
+ Float aMaxBorderRadius)
+{
+ // If dashLength is not calculateable, find it with binary search,
+ // such that there exists i that OuterP_i == OuterP_n and
+ // InnerP_i == InnerP_n with given dashLength.
+
+ FourFloats key(aMinBorderWidth, aMaxBorderWidth,
+ aMinBorderRadius, aMaxBorderRadius);
+ BestDashLength best;
+ if (DashedCornerCache.Get(key, &best)) {
+ mCount = best.count;
+ mBestDashLength = best.dashLength;
+ return;
+ }
+
+ Float lower = 1.0f;
+ Float upper = DOT_LENGTH * DASH_LENGTH;
+ Float dashLength = upper;
+ size_t targetCount = 0;
+
+ const Float LENGTH_MARGIN = 0.1f;
+ for (size_t j = 0; j < MAX_LOOP; j++) {
+ size_t count;
+ Float actualDashLength;
+ if (!GetCountAndLastDashLength(dashLength, &count, &actualDashLength)) {
+ if (j == 0) {
+ mCount = mMaxCount;
+ break;
+ }
+ }
+
+ if (j == 0) {
+ if (count == 1) {
+ // If only 1 segment fits, fill entire region
+ //
+ // count = 1
+ // mCount = 1
+ // | 1 |
+ // +---+---+
+ // |###|###|
+ // |###|###|
+ // |###|###|
+ // +---+---+
+ // 1
+ mCount = 1;
+ break;
+ }
+
+ // targetCount should be 2n.
+ //
+ // targetCount = 2
+ // mCount = 2
+ // | 1 | 2 |
+ // +---+---+---+---+
+ // |###| | |###|
+ // |###| | |###|
+ // |###| | |###|
+ // +---+---+---+---+
+ // 1 2
+ //
+ // targetCount = 6
+ // mCount = 4
+ // | 1 | 2 | 3 | 4 | 5 | 6 |
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // |###| | |###|###| | |###|###| | |###|
+ // |###| | |###|###| | |###|###| | |###|
+ // |###| | |###|###| | |###|###| | |###|
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // 1 2 3 4
+ if (count % 2) {
+ targetCount = count + 1;
+ } else {
+ targetCount = count;
+ }
+
+ mCount = targetCount / 2 + 1;
+ }
+
+ if (count == targetCount) {
+ mBestDashLength = dashLength;
+
+ // actualDashLength won't be greater than dashLength.
+ if (actualDashLength > dashLength - LENGTH_MARGIN) {
+ break;
+ }
+
+ // We started from upper bound, no need to update range when j == 0.
+ if (j > 0) {
+ upper = dashLength;
+ }
+ } else {
+ // |j == 0 && count != targetCount| means that |targetCount = count + 1|,
+ // and we started from upper bound, no need to update range when j == 0.
+ if (j > 0) {
+ if (count > targetCount) {
+ lower = dashLength;
+ } else {
+ upper = dashLength;
+ }
+ }
+ }
+
+ dashLength = (upper + lower) / 2.0f;
+ }
+
+ if (DashedCornerCache.Count() > DashedCornerCacheSize) {
+ DashedCornerCache.Clear();
+ }
+ DashedCornerCache.Put(key, BestDashLength(mBestDashLength, mCount));
+}
+
+bool
+DashedCornerFinder::GetCountAndLastDashLength(Float aDashLength,
+ size_t* aCount,
+ Float* aActualDashLength)
+{
+ // Return the number of segments and the last segment's dashLength for
+ // the given dashLength.
+
+ Reset();
+
+ for (size_t i = 0; i < mMaxCount; i++) {
+ Float actualDashLength = FindNext(aDashLength);
+ if (mLastOuterT >= 1.0f) {
+ *aCount = i + 1;
+ *aActualDashLength = actualDashLength;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/DashedCornerFinder.h
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DashedCornerFinder_h_
+#define mozilla_DashedCornerFinder_h_
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BezierUtils.h"
+
+namespace mozilla {
+
+// Calculate {OuterT_i, InnerT_i} for each 1 < i < n, that
+// (OuterL_i + InnerL_i) / 2 == dashLength * (W_i + W_{i-1}) / 2
+// where
+// OuterP_i: OuterCurve(OuterT_i)
+// InnerP_i: InnerCurve(OuterT_i)
+// OuterL_i: Elliptic arc length between OuterP_i - OuterP_{i-1}
+// InnerL_i: Elliptic arc length between InnerP_i - InnerP_{i-1}
+// W_i = |OuterP_i - InnerP_i|
+// 1.0 < dashLength < 3.0
+//
+// OuterP_1 OuterP_0
+// _+__-----------+ OuterCurve
+// OuterP_2 __---- | OuterL_1 |
+// __+--- | |
+// __--- | OuterL_2 | |
+// OuterP_3 _-- | | W_1 | W_0
+// _+ | | |
+// / \ W_2 | | |
+// / \ | | InnerL_1 |
+// | \ | InnerL_2|____-------+ InnerCurve
+// | \ |____----+ InnerP_0
+// | . \ __---+ InnerP_1
+// | \ / InnerP_2
+// | . /+ InnerP_3
+// | |
+// | . |
+// | |
+// | |
+// | |
+// OuterP_{n-1} +--------+ InnerP_{n-1}
+// | |
+// | |
+// | |
+// | |
+// | |
+// OuterP_n +--------+ InnerP_n
+//
+// Returns region with [OuterCurve((OuterT_{2j} + OuterT_{2j-1}) / 2),
+// OuterCurve((OuterT_{2j} + OuterT_{2j-1}) / 2),
+// InnerCurve((OuterT_{2j} + OuterT_{2j+1}) / 2),
+// InnerCurve((OuterT_{2j} + OuterT_{2j+1}) / 2)],
+// to start and end with half segment.
+//
+// _+__----+------+ OuterCurve
+// _+---- | |######|
+// __+---#| | |######|
+// _+---##|####| | |######|
+// _-- |#####|#####| | |#####|
+// _+ |#####|#####| | |#####|
+// / \ |#####|####| | |#####|
+// / \ |####|#####| | |#####|
+// | \ |####|####| |____-+-----+ InnerCurve
+// | \ |####|____+---+
+// | . \ __+---+
+// | \ /
+// | . /+
+// | |
+// | . |
+// | |
+// | |
+// | |
+// +--------+
+// | |
+// | |
+// +--------+
+// |########|
+// |########|
+// +--------+
+
+class DashedCornerFinder
+{
+ typedef mozilla::gfx::Bezier Bezier;
+ typedef mozilla::gfx::Float Float;
+ typedef mozilla::gfx::Point Point;
+ typedef mozilla::gfx::Size Size;
+
+public:
+ struct Result
+ {
+ // Control points for the outer curve and the inner curve.
+ //
+ // outerSectionBezier
+ // |
+ // v _+ 3
+ // ___---#|
+ // 0 +---#######|
+ // |###########|
+ // |###########|
+ // |##########|
+ // |##########|
+ // |#########|
+ // |#####____+ 3
+ // 0 +----
+ // ^
+ // |
+ // innerSectionBezier
+ Bezier outerSectionBezier;
+ Bezier innerSectionBezier;
+
+ Result(const Bezier& aOuterSectionBezier,
+ const Bezier& aInnerSectionBezier)
+ : outerSectionBezier(aOuterSectionBezier),
+ innerSectionBezier(aInnerSectionBezier)
+ {}
+ };
+
+ // aCornerDim.width
+ // |<----------------->|
+ // | |
+ // --+-------------___---+--
+ // ^ | __-- | ^
+ // | | _- | |
+ // | | / | | aBorderWidthH
+ // | | / | |
+ // | | | | v
+ // | | | __--+--
+ // aCornerDim.height | || _-
+ // | || /
+ // | | /
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // v | |
+ // --+---------+
+ // | |
+ // |<------->|
+ // aBorderWidthV
+ DashedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier,
+ Float aBorderWidthH, Float aBorderWidthV,
+ const Size& aCornerDim);
+
+ bool HasMore(void) const;
+ Result Next(void);
+
+private:
+ static const size_t MAX_LOOP = 32;
+
+ // Bezier control points for the outer curve and the inner curve.
+ //
+ // ___---+ outer curve
+ // __-- |
+ // _- |
+ // / |
+ // / |
+ // | |
+ // | __--+ inner curve
+ // | _-
+ // | /
+ // | /
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // +---------+
+ Bezier mOuterBezier;
+ Bezier mInnerBezier;
+
+ Point mLastOuterP;
+ Point mLastInnerP;
+ Float mLastOuterT;
+ Float mLastInnerT;
+
+ // Length for each segment, ratio of the border width at that point.
+ Float mBestDashLength;
+
+ // If one of border-widths is 0, do not calculate mBestDashLength, and draw
+ // segments until it reaches the other side or exceeds mMaxCount.
+ bool mHasZeroBorderWidth;
+ bool mHasMore;
+
+ // The maximum number of segments.
+ size_t mMaxCount;
+
+ enum {
+ // radius.width
+ // |<----------------->|
+ // | |
+ // --+-------------___---+--
+ // ^ | __-- | ^
+ // | | _- | |
+ // | | / + | top-width
+ // | | / | |
+ // | | | | v
+ // | | | __--+--
+ // radius.height | || _-
+ // | || /
+ // | | /
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // v | |
+ // --+----+----+
+ // | |
+ // |<------->|
+ // left-width
+
+ // * top-width == left-width
+ // * radius.width == radius.height
+ // * top-width < radius.width * 2
+ //
+ // Split the perfect circle's arc into 2n segments, each segment's length is
+ // top-width * dashLength. Then split the inner curve and the outer curve
+ // with same angles.
+ //
+ // radius.width
+ // |<---------------------->|
+ // | | v
+ // --+------------------------+--
+ // ^ | | | top-width / 2
+ // | | perfect | |
+ // | | circle's ___---+--
+ // | | arc __-+ | ^
+ // | | | _- | |
+ // radius.height | | | + | +--
+ // | | | / \ | |
+ // | | | | \ | |
+ // | | | | \ | |
+ // | | +->| \ | |
+ // | | +---__ \ | |
+ // | | | --__ \ | |
+ // | | | ---__ \ | |
+ // v | | --_\||
+ // --+----+----+--------------+
+ // | | |
+ // |<-->| |
+ // left-width / 2
+ PERFECT,
+
+ // Other cases.
+ //
+ // Split the outer curve and the inner curve into 2n segments, each segment
+ // satisfies following:
+ // (OuterL_i + InnerL_i) / 2 == dashLength * (W_i + W_{i-1}) / 2
+ OTHER
+ } mType;
+
+ size_t mI;
+ size_t mCount;
+
+ // Determine mType from parameters.
+ void DetermineType(Float aBorderWidthH, Float aBorderWidthV);
+
+ // Reset calculation.
+ void Reset(void);
+
+ // Find next segment.
+ Float FindNext(Float dashLength);
+
+ // Find mBestDashLength for parameters.
+ void FindBestDashLength(Float aMinBorderWidth, Float aMaxBorderWidth,
+ Float aMinBorderRadius, Float aMaxBorderRadius);
+
+ // Fill corner with dashes with given dash length, and return the number of
+ // segments and last segment's dash length.
+ bool GetCountAndLastDashLength(Float aDashLength,
+ size_t* aCount, Float* aActualDashLength);
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_DashedCornerFinder_h_ */
new file mode 100644
--- /dev/null
+++ b/layout/base/DottedCornerFinder.cpp
@@ -0,0 +1,553 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DottedCornerFinder.h"
+
+#include "mozilla/Move.h"
+#include "BorderCache.h"
+#include "BorderConsts.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+static inline Float
+Square(Float x)
+{
+ return x * x;
+}
+
+static Point
+PointRotateCCW90(const Point& aP)
+{
+ return Point(aP.y, -aP.x);
+}
+
+struct BestOverlap
+{
+ Float overlap;
+ size_t count;
+
+ BestOverlap()
+ : overlap(0.0f), count(0)
+ {}
+
+ BestOverlap(Float aOverlap, size_t aCount)
+ : overlap(aOverlap), count(aCount)
+ {}
+};
+
+static const size_t DottedCornerCacheSize = 256;
+nsDataHashtable<FourFloatsHashKey, BestOverlap> DottedCornerCache;
+
+DottedCornerFinder::DottedCornerFinder(const Bezier& aOuterBezier,
+ const Bezier& aInnerBezier,
+ mozilla::css::Corner aCorner,
+ Float aBorderRadiusX,
+ Float aBorderRadiusY,
+ const Point& aC0, Float aR0,
+ const Point& aCn, Float aRn,
+ const Size& aCornerDim)
+ : mOuterBezier(aOuterBezier),
+ mInnerBezier(aInnerBezier),
+ mCorner(aCorner),
+ mNormalSign((aCorner == C_TL || aCorner == C_BR) ? -1.0f : 1.0f),
+ mC0(aC0), mCn(aCn),
+ mR0(aR0), mRn(aRn), mMaxR(std::max(aR0, aRn)),
+ mCurveOrigin(mC0.x, mCn.y),
+ mBestOverlap(0.0f),
+ mHasZeroBorderWidth(false), mHasMore(true),
+ mMaxCount(aCornerDim.width + aCornerDim.height),
+ mType(OTHER),
+ mI(0), mCount(0)
+{
+ NS_ASSERTION(mR0 > 0.0f || mRn > 0.0f,
+ "At least one side should have non-zero radius.");
+
+ mInnerWidth = fabs(mInnerBezier.mPoints[0].x - mInnerBezier.mPoints[3].x);
+ mInnerHeight = fabs(mInnerBezier.mPoints[0].y - mInnerBezier.mPoints[3].y);
+
+ DetermineType(aBorderRadiusX, aBorderRadiusY);
+
+ Reset();
+}
+
+static bool
+IsSingleCurve(Float aMinR, Float aMaxR,
+ Float aMinBorderRadius, Float aMaxBorderRadius)
+{
+ return aMinR > 0.0f &&
+ aMinBorderRadius > aMaxR * 4.0f &&
+ aMinBorderRadius / aMaxBorderRadius > 0.5f;
+}
+
+void
+DottedCornerFinder::DetermineType(Float aBorderRadiusX, Float aBorderRadiusY)
+{
+ // Calculate parameters for the center curve before swap.
+ Float centerCurveWidth = fabs(mC0.x - mCn.x);
+ Float centerCurveHeight = fabs(mC0.y - mCn.y);
+ Point cornerPoint(mCn.x, mC0.y);
+
+ bool swapped = false;
+ if (mR0 < mRn) {
+ // Always draw from wider side to thinner side.
+ Swap(mC0, mCn);
+ Swap(mR0, mRn);
+ Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
+ Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
+ Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
+ Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
+ mNormalSign = -mNormalSign;
+ swapped = true;
+ }
+
+ // See the comment at mType declaration for each condition.
+
+ Float minR = std::min(mR0, mRn);
+ Float minBorderRadius = std::min(aBorderRadiusX, aBorderRadiusY);
+ Float maxBorderRadius = std::max(aBorderRadiusX, aBorderRadiusY);
+ if (IsSingleCurve(minR, mMaxR, minBorderRadius, maxBorderRadius)) {
+ if (mR0 == mRn) {
+ Float borderLength;
+ if (minBorderRadius == maxBorderRadius) {
+ mType = PERFECT;
+ borderLength = M_PI * centerCurveHeight / 2.0f;
+
+ mCenterCurveR = centerCurveWidth;
+ } else {
+ mType = SINGLE_CURVE_AND_RADIUS;
+ borderLength = GetQuarterEllipticArcLength(centerCurveWidth,
+ centerCurveHeight);
+ }
+
+ Float diameter = mR0 * 2.0f;
+ size_t count = round(borderLength / diameter);
+ if (count % 2) {
+ count++;
+ }
+ mCount = count / 2 - 1;
+ if (mCount > 0) {
+ mBestOverlap = 1.0f - borderLength / (diameter * count);
+ }
+ } else {
+ mType = SINGLE_CURVE;
+ }
+ }
+
+ if (mType == SINGLE_CURVE_AND_RADIUS || mType == SINGLE_CURVE) {
+ Size cornerSize(centerCurveWidth, centerCurveHeight);
+ GetBezierPointsForCorner(&mCenterBezier, mCorner,
+ cornerPoint, cornerSize);
+ if (swapped) {
+ Swap(mCenterBezier.mPoints[0], mCenterBezier.mPoints[3]);
+ Swap(mCenterBezier.mPoints[1], mCenterBezier.mPoints[2]);
+ }
+ }
+
+ if (minR == 0.0f) {
+ mHasZeroBorderWidth = true;
+ }
+
+ if ((mType == SINGLE_CURVE || mType == OTHER) && !mHasZeroBorderWidth) {
+ FindBestOverlap(minR, minBorderRadius, maxBorderRadius);
+ }
+}
+
+
+bool
+DottedCornerFinder::HasMore(void) const
+{
+ if (mHasZeroBorderWidth) {
+ return mI < mMaxCount && mHasMore;
+ }
+
+ return mI < mCount;
+}
+
+DottedCornerFinder::Result
+DottedCornerFinder::Next(void)
+{
+ mI++;
+
+ if (mType == PERFECT) {
+ Float phi = mI * 4.0f * mR0 * (1 - mBestOverlap) / mCenterCurveR;
+ if (mCorner == C_TL) {
+ phi = -M_PI / 2.0f - phi;
+ } else if (mCorner == C_TR) {
+ phi = -M_PI / 2.0f + phi;
+ } else if (mCorner == C_BR) {
+ phi = M_PI / 2.0f - phi;
+ } else {
+ phi = M_PI / 2.0f + phi;
+ }
+
+ Point C(mCurveOrigin.x + mCenterCurveR * cos(phi),
+ mCurveOrigin.y + mCenterCurveR * sin(phi));
+ return DottedCornerFinder::Result(C, mR0);
+ }
+
+ // Find unfilled and filled circles.
+ (void)FindNext(mBestOverlap);
+ (void)FindNext(mBestOverlap);
+ return Result(mLastC, mLastR);
+}
+
+void
+DottedCornerFinder::Reset(void)
+{
+ mLastC = mC0;
+ mLastR = mR0;
+ mLastT = 0.0f;
+ mHasMore = true;
+}
+
+void
+DottedCornerFinder::FindPointAndRadius(Point& C, Float& r,
+ const Point& innerTangent,
+ const Point& normal, Float t)
+{
+ // Find radius for the given tangent point on the inner curve such that the
+ // circle is also tangent to the outer curve.
+
+ NS_ASSERTION(mType == OTHER, "Wrong mType");
+
+ Float lower = 0.0f;
+ Float upper = mMaxR;
+ const Float DIST_MARGIN = 0.1f;
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ r = (upper + lower) / 2.0f;
+ C = innerTangent + normal * r;
+
+ Point Near = FindBezierNearestPoint(mOuterBezier, C, t);
+ Float distSquare = (C - Near).LengthSquare();
+
+ if (distSquare > Square(r + DIST_MARGIN)) {
+ lower = r;
+ } else if (distSquare < Square(r - DIST_MARGIN)) {
+ upper = r;
+ } else {
+ break;
+ }
+ }
+}
+
+Float
+DottedCornerFinder::FindNext(Float overlap)
+{
+ Float lower = mLastT;
+ Float upper = 1.0f;
+ Float t;
+
+ Point C = mLastC;
+ Float r = 0.0f;
+
+ Float factor = (1.0f - overlap);
+
+ Float circlesDist = 0.0f;
+ Float expectedDist = 0.0f;
+
+ const Float DIST_MARGIN = 0.1f;
+ if (mType == SINGLE_CURVE_AND_RADIUS) {
+ r = mR0;
+
+ expectedDist = (r + mLastR) * factor;
+
+ // Find C_i on the center curve.
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ t = (upper + lower) / 2.0f;
+ C = GetBezierPoint(mCenterBezier, t);
+
+ // Check overlap along arc.
+ circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
+ if (circlesDist < expectedDist - DIST_MARGIN) {
+ lower = t;
+ } else if (circlesDist > expectedDist + DIST_MARGIN) {
+ upper = t;
+ } else {
+ break;
+ }
+ }
+ } else if (mType == SINGLE_CURVE) {
+ // Find C_i on the center curve, and calculate r_i.
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ t = (upper + lower) / 2.0f;
+ C = GetBezierPoint(mCenterBezier, t);
+
+ Point Diff = GetBezierDifferential(mCenterBezier, t);
+ Float DiffLength = Diff.Length();
+ if (DiffLength == 0.0f) {
+ // Basically this shouldn't happen.
+ // If differential is 0, we cannot calculate tangent circle,
+ // skip this point.
+ t = (t + upper) / 2.0f;
+ continue;
+ }
+
+ Point normal = PointRotateCCW90(Diff / DiffLength) * (-mNormalSign);
+ r = CalculateDistanceToEllipticArc(C, normal, mCurveOrigin,
+ mInnerWidth, mInnerHeight);
+
+ // Check overlap along arc.
+ circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
+ expectedDist = (r + mLastR) * factor;
+ if (circlesDist < expectedDist - DIST_MARGIN) {
+ lower = t;
+ } else if (circlesDist > expectedDist + DIST_MARGIN) {
+ upper = t;
+ } else {
+ break;
+ }
+ }
+ } else {
+ Float distSquareMax = Square(mMaxR * 3.0f);
+ Float circlesDistSquare = 0.0f;
+
+ // Find C_i and r_i.
+ for (size_t i = 0; i < MAX_LOOP; i++) {
+ t = (upper + lower) / 2.0f;
+ Point innerTangent = GetBezierPoint(mInnerBezier, t);
+ if ((innerTangent - mLastC).LengthSquare() > distSquareMax) {
+ // It's clear that this tangent point is too far, skip it.
+ upper = t;
+ continue;
+ }
+
+ Point Diff = GetBezierDifferential(mInnerBezier, t);
+ Float DiffLength = Diff.Length();
+ if (DiffLength == 0.0f) {
+ // Basically this shouldn't happen.
+ // If differential is 0, we cannot calculate tangent circle,
+ // skip this point.
+ t = (t + upper) / 2.0f;
+ continue;
+ }
+
+ Point normal = PointRotateCCW90(Diff / DiffLength) * mNormalSign;
+ FindPointAndRadius(C, r, innerTangent, normal, t);
+
+ // Check overlap with direct distance.
+ circlesDistSquare = (C - mLastC).LengthSquare();
+ expectedDist = (r + mLastR) * factor;
+ if (circlesDistSquare < Square(expectedDist - DIST_MARGIN)) {
+ lower = t;
+ } else if (circlesDistSquare > Square(expectedDist + DIST_MARGIN)) {
+ upper = t;
+ } else {
+ break;
+ }
+ }
+
+ circlesDist = sqrt(circlesDistSquare);
+ }
+
+ mLastT = t;
+ mLastC = C;
+ mLastR = r;
+
+ if (mHasZeroBorderWidth) {
+ const Float T_MARGIN = 0.001f;
+ if (mLastT >= 1.0f - T_MARGIN ||
+ (mLastC - mCn).LengthSquare() < Square(mLastR)) {
+ mHasMore = false;
+ }
+ }
+
+ if (expectedDist == 0.0f) {
+ return 0.0f;
+ }
+
+ return 1.0f - circlesDist * factor / expectedDist;
+}
+
+void
+DottedCornerFinder::FindBestOverlap(Float aMinR, Float aMinBorderRadius,
+ Float aMaxBorderRadius)
+{
+ // If overlap is not calculateable, find it with binary search,
+ // such that there exists i that C_i == C_n with the given overlap.
+
+ FourFloats key(aMinR, mMaxR,
+ aMinBorderRadius, aMaxBorderRadius);
+ BestOverlap best;
+ if (DottedCornerCache.Get(key, &best)) {
+ mCount = best.count;
+ mBestOverlap = best.overlap;
+ return;
+ }
+
+ Float lower = 0.0f;
+ Float upper = 0.5f;
+ // Start from lower bound to find the minimum number of circles.
+ Float overlap = 0.0f;
+ mBestOverlap = overlap;
+ size_t targetCount = 0;
+
+ const Float OVERLAP_MARGIN = 0.1f;
+ for (size_t j = 0; j < MAX_LOOP; j++) {
+ Reset();
+
+ size_t count;
+ Float actualOverlap;
+ if (!GetCountAndLastOverlap(overlap, &count, &actualOverlap)) {
+ if (j == 0) {
+ mCount = mMaxCount;
+ break;
+ }
+ }
+
+ if (j == 0) {
+ if (count < 3 || (count == 3 && actualOverlap > 0.5f)) {
+ // |count == 3 && actualOverlap > 0.5f| means there could be
+ // a circle but it is too near from both ends.
+ //
+ // if actualOverlap == 0.0
+ // 1 2 3
+ // +-------+-------+-------+-------+
+ // | ##### | ***** | ##### | ##### |
+ // |#######|*******|#######|#######|
+ // |###+###|***+***|###+###|###+###|
+ // |# C_0 #|* C_1 *|# C_2 #|# C_n #|
+ // | ##### | ***** | ##### | ##### |
+ // +-------+-------+-------+-------+
+ // |
+ // V
+ // +-------+---+-------+---+-------+
+ // | ##### | | ##### | | ##### |
+ // |#######| |#######| |#######|
+ // |###+###| |###+###| |###+###| Find the best overlap to place
+ // |# C_0 #| |# C_1 #| |# C_n #| C_1 at the middle of them
+ // | ##### | | ##### | | ##### |
+ // +-------+---+-------+---|-------+
+ //
+ // if actualOverlap == 0.5
+ // 1 2 3
+ // +-------+-------+-------+---+
+ // | ##### | ***** | ##### |## |
+ // |#######|*******|##### C_n #|
+ // |###+###|***+***|###+###+###|
+ // |# C_0 #|* C_1 *|# C_2 #|###|
+ // | ##### | ***** | ##### |## |
+ // +-------+-------+-------+---+
+ // |
+ // V
+ // +-------+-+-------+-+-------+
+ // | ##### | | ##### | | ##### |
+ // |#######| |#######| |#######|
+ // |###+###| |###+###| |###+###| Even if we place C_1 at the middle
+ // |# C_0 #| |# C_1 #| |# C_n #| of them, it's too near from them
+ // | ##### | | ##### | | ##### |
+ // +-------+-+-------+-|-------+
+ // |
+ // V
+ // +-------+-----------+-------+
+ // | ##### | | ##### |
+ // |#######| |#######|
+ // |###+###| |###+###| Do not draw any circle
+ // |# C_0 #| |# C_n #|
+ // | ##### | | ##### |
+ // +-------+-----------+-------+
+ mCount = 0;
+ break;
+ }
+
+ // targetCount should be 2n, as we're searching C_1 to C_n.
+ //
+ // targetCount = 4
+ // mCount = 1
+ // 1 2 3 4
+ // +-------+-------+-------+-------+-------+
+ // | ##### | ***** | ##### | ***** | ##### |
+ // |#######|*******|#######|*******|#######|
+ // |###+###|***+***|###+###|***+***|###+###|
+ // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_n #|
+ // | ##### | ***** | ##### | ***** | ##### |
+ // +-------+-------+-------+-------+-------+
+ // 1
+ //
+ // targetCount = 6
+ // mCount = 2
+ // 1 2 3 4 5 6
+ // +-------+-------+-------+-------+-------+-------+-------+
+ // | ##### | ***** | ##### | ***** | ##### | ***** | ##### |
+ // |#######|*******|#######|*******|#######|*******|#######|
+ // |###+###|***+***|###+###|***+***|###+###|***+***|###+###|
+ // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_4 #|* C_5 *|# C_n #|
+ // | ##### | ***** | ##### | ***** | ##### | ***** | ##### |
+ // +-------+-------+-------+-------+-------+-------+-------+
+ // 1 2
+ if (count % 2) {
+ targetCount = count + 1;
+ } else {
+ targetCount = count;
+ }
+
+ mCount = targetCount / 2 - 1;
+ }
+
+ if (count == targetCount) {
+ mBestOverlap = overlap;
+
+ if (fabs(actualOverlap - overlap) < OVERLAP_MARGIN) {
+ break;
+ }
+
+ // We started from upper bound, no need to update range when j == 0.
+ if (j > 0) {
+ if (actualOverlap > overlap) {
+ lower = overlap;
+ } else {
+ upper = overlap;
+ }
+ }
+ } else {
+ // |j == 0 && count != targetCount| means that |targetCount = count + 1|,
+ // and we started from upper bound, no need to update range when j == 0.
+ if (j > 0) {
+ if (count > targetCount) {
+ upper = overlap;
+ } else {
+ lower = overlap;
+ }
+ }
+ }
+
+ overlap = (upper + lower) / 2.0f;
+ }
+
+ if (DottedCornerCache.Count() > DottedCornerCacheSize) {
+ DottedCornerCache.Clear();
+ }
+ DottedCornerCache.Put(key, BestOverlap(mBestOverlap, mCount));
+}
+
+bool
+DottedCornerFinder::GetCountAndLastOverlap(Float aOverlap,
+ size_t* aCount,
+ Float* aActualOverlap)
+{
+ // Return the number of circles and the last circles' overlap for the
+ // given overlap.
+
+ Reset();
+
+ const Float T_MARGIN = 0.001f;
+ const Float DIST_MARGIN = 0.1f;
+ const Float DIST_MARGIN_SQUARE = Square(DIST_MARGIN);
+ for (size_t i = 0; i < mMaxCount; i++) {
+ Float actualOverlap = FindNext(aOverlap);
+ if (mLastT >= 1.0f - T_MARGIN ||
+ (mLastC - mCn).LengthSquare() < DIST_MARGIN_SQUARE) {
+ *aCount = i + 1;
+ *aActualOverlap = actualOverlap;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/DottedCornerFinder.h
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DottedCornerFinder_h_
+#define mozilla_DottedCornerFinder_h_
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BezierUtils.h"
+#include "gfxRect.h"
+
+namespace mozilla {
+
+// Calculate C_i and r_i for each filled/unfilled circles in dotted corner.
+// Returns circle with C_{2j} and r_{2j} where 0 < 2j < n.
+//
+// ____-----------+
+// __---- ***** ###|
+// __---- ********* ####|
+// __--- ##### ***********#####|
+// _-- ######### *****+*****#####+ C_0
+// _- ########### *** C_1****#####|
+// / #####+##### ********* ####|
+// / . ### C_2 ### ***** ###|
+// | ######### ____-------+
+// | . #####____-----
+// | __----
+// | . /
+// | /
+// | ***** |
+// | ******* |
+// |*********|
+// |****+****|
+// | C_{n-1} |
+// | ******* |
+// | ***** |
+// | ##### |
+// | ####### |
+// |#########|
+// +----+----+
+// C_n
+
+class DottedCornerFinder
+{
+ typedef mozilla::gfx::Bezier Bezier;
+ typedef mozilla::gfx::Float Float;
+ typedef mozilla::gfx::Point Point;
+ typedef mozilla::gfx::Size Size;
+
+public:
+ struct Result
+ {
+ // Center point of dot and its radius.
+ Point C;
+ Float r;
+
+ Result(const Point& aC, Float aR)
+ : C(aC), r(aR)
+ {}
+ };
+
+ // aBorderRadiusX
+ // aCornerDim.width
+ // |<----------------->|
+ // | | v
+ // --+-------------___---+--
+ // ^ | __-- | |
+ // | | _- | | aR0
+ // | | / aC0 +--
+ // | | / | ^
+ // | | | |
+ // aBorderRadiusY | | | __--+
+ // aCornerDim.height | || _-
+ // | || /
+ // | | /
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // v | aCn |
+ // --+----+----+
+ // | |
+ // |<-->|
+ // aRn
+ //
+ // aCornerDim and (aBorderRadiusX, aBorderRadiusY) can be different when
+ // aBorderRadiusX is smaller than aRn*2 or
+ // aBorderRadiusY is smaller than aR0*2.
+ //
+ // aCornerDim.width
+ // |<----------------->|
+ // | |
+ // | aBorderRadiusX |
+ // |<--------->| |
+ // | | |
+ // -------------------+-------__--+-------+--
+ // ^ ^ | _- | ^
+ // | | | / | |
+ // | | | / | |
+ // | aBorderRadiusY | | | | | aR0
+ // | | || | |
+ // | | | | |
+ // aCornerDim.height | v | | v
+ // | --+ aC0 +--
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // v | aCn |
+ // -------------------+---------+---------+
+ // | |
+ // |<------->|
+ // aRn
+ DottedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier,
+ mozilla::css::Corner aCorner,
+ Float aBorderRadiusX, Float aBorderRadiusY,
+ const Point& aC0, Float aR0, const Point& aCn, Float aRn,
+ const Size& aCornerDim);
+
+ bool HasMore(void) const;
+ Result Next(void);
+
+private:
+ static const size_t MAX_LOOP = 32;
+
+ // Bezier control points for the outer curve, the inner curve, and a curve
+ // that center points of circles are on (center curve).
+ //
+ // ___---+ outer curve
+ // __-- |
+ // _- |
+ // / __---+ center curve
+ // / __-- |
+ // | / |
+ // | / __--+ inner curve
+ // | | _-
+ // | | /
+ // | | /
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // | | |
+ // +----+----+
+ Bezier mOuterBezier;
+ Bezier mInnerBezier;
+ Bezier mCenterBezier;
+
+ mozilla::css::Corner mCorner;
+
+ // Sign of the normal vector used in radius calculation, flipped depends on
+ // corner and start and end radii.
+ Float mNormalSign;
+
+ // Center points and raii for start and end circles, mR0 >= mRn.
+ // mMaxR = max(mR0, mRn)
+ //
+ // v
+ // ___---+------
+ // __-- #|# | mRn
+ // _- ##|## |
+ // / ##+## ---
+ // / mCn ^
+ // | #|#
+ // | __--+
+ // | _-
+ // | /
+ // | /
+ // | |
+ // | |
+ // | ##### |
+ // | ####### |
+ // |#########|
+ // +----+----+
+ // |## mC0 ##|
+ // | ####### |
+ // | ##### |
+ // | |
+ // |<-->|
+ //
+ // mR0
+ //
+ Point mC0;
+ Point mCn;
+ Float mR0;
+ Float mRn;
+ Float mMaxR;
+
+ // Parameters for the center curve with perfect circle and the inner curve.
+ //
+ // ___---+
+ // __-- |
+ // _- |
+ // / __---+
+ // / __-- |
+ // | / |
+ // | / __--+--
+ // | | _- | ^
+ // | | / | |
+ // | | / | |
+ // | | | | |
+ // | | | | | mInnerHeight
+ // | | | | |
+ // | | | | |
+ // | | | | v
+ // +----+----+---------+
+ // | | | mCurveOrigin
+ // | |<------->|
+ // | mInnerWidth |
+ // | |
+ // |<------------>|
+ // mCenterCurveR
+ //
+ Point mCurveOrigin;
+ Float mCenterCurveR;
+ Float mInnerWidth;
+ Float mInnerHeight;
+
+ Point mLastC;
+ Float mLastR;
+ Float mLastT;
+
+ // Overlap between two circles.
+ // It uses arc length on PERFECT, SINGLE_CURVE_AND_RADIUS, and SINGLE_CURVE,
+ // and direct distance on OTHER.
+ Float mBestOverlap;
+
+ // If one of border-widths is 0, do not calculate overlap, and draw circles
+ // until it reaches the other side or exceeds mMaxCount.
+ bool mHasZeroBorderWidth;
+ bool mHasMore;
+
+ // The maximum number of filled/unfilled circles.
+ size_t mMaxCount;
+
+ enum {
+ // radius.width
+ // |<----------------->|
+ // | |
+ // --+-------------___---+----
+ // ^ | __-- #|# ^
+ // | | _- ##|## |
+ // | | / ##+## | top-width
+ // | | / ##|## |
+ // | | | #|# v
+ // | | | __--+----
+ // radius.height | || _-
+ // | || /
+ // | | /
+ // | | |
+ // | | |
+ // | | ##### |
+ // | | ####### |
+ // v |#########|
+ // --+----+----+
+ // |#########|
+ // | ####### |
+ // | ##### |
+ // | |
+ // |<------->|
+ // left-width
+
+ // * top-width == left-width
+ // * radius.width == radius.height
+ // * top-width < radius.width * 2
+ //
+ // All circles has same radii and are on single perfect circle's arc.
+ // Overlap is known.
+ //
+ // Split the perfect circle's arc into 2n segments, each segment's length is
+ // top-width * (1 - overlap). Place each circle's center point C_i on each
+ // end of the segment, each circle's radius r_i is top-width / 2
+ //
+ // #####
+ // #######
+ // perfect #########
+ // circle's ___---+####
+ // arc ##### __-- ## C_0 ##
+ // | #####_- ###|###
+ // | ####+#### ##|##
+ // | ##/C_i ## |
+ // | |###### |
+ // | | ##### |
+ // +->| |
+ // | |
+ // ##|## |
+ // ###|### |
+ // ####|#### |
+ // ####+-------------------+
+ // ## C_n ##
+ // #######
+ // #####
+ PERFECT,
+
+ // * top-width == left-width
+ // * 0.5 < radius.width / radius.height < 2.0
+ // * top-width < min(radius.width, radius.height) * 2
+ //
+ // All circles has same radii and are on single elliptic arc.
+ // Overlap is known.
+ //
+ // Split the elliptic arc into 2n segments, each segment's length is
+ // top-width * (1 - overlap). Place each circle's center point C_i on each
+ // end of the segment, each circle's radius r_i is top-width / 2
+ //
+ // #####
+ // #######
+ // ##### #########
+ // ####### ____----+####
+ // elliptic ######__--- ## C_0 ##
+ // arc ##__+-### ###|###
+ // | / # C_i # ##|##
+ // +--> / ##### |
+ // | |
+ // ###|# |
+ // ###|### |
+ // ####|#### |
+ // ####+------------------------+
+ // ## C_n ##
+ // #######
+ // #####
+ SINGLE_CURVE_AND_RADIUS,
+
+ // * top-width != left-width
+ // * 0 < min(top-width, left-width)
+ // * 0.5 < radius.width / radius.height < 2.0
+ // * max(top-width, left-width) < min(radius.width, radius.height) * 2
+ //
+ // All circles are on single elliptic arc.
+ // Overlap is unknown.
+ //
+ // Place each circle's center point C_i on elliptic arc, each circle's
+ // radius r_i is the distance between the center point and the inner curve.
+ // The arc segment's length between C_i and C_{i-1} is
+ // (r_i + r_{i-1}) * (1 - overlap).
+ //
+ // outer curve
+ // /
+ // /
+ // / / center curve
+ // / ####### /
+ // /## /#
+ // +# / #
+ // /# / #
+ // / # C_i / #
+ // / # + # /
+ // / # / \ # / inner curve
+ // # / \ #/
+ // # / r_i \+
+ // #/ ##/
+ // / ####### /
+ // /
+ SINGLE_CURVE,
+
+ // Other cases.
+ // Circles are not on single elliptic arc.
+ // Overlap are unknown.
+ //
+ // Place tangent point innerTangent on the inner curve and find circle's
+ // center point C_i and radius r_i where the circle is also tangent to the
+ // outer curve.
+ // Distance between C_i and C_{i-1} is (r_i + r_{i-1}) * (1 - overlap).
+ //
+ // outer curve
+ // /
+ // /
+ // /
+ // / #######
+ // /## ##
+ // +# #
+ // /# \ #
+ // / # \ #
+ // / # + # /
+ // / # C_i \ # / inner curve
+ // # \ #/
+ // # r_i \+
+ // ## ##/ innerTangent
+ // ####### /
+ // /
+ OTHER
+ } mType;
+
+ size_t mI;
+ size_t mCount;
+
+ // Determine mType from parameters.
+ void DetermineType(Float aBorderRadiusX, Float aBorderRadiusY);
+
+ // Reset calculation.
+ void Reset(void);
+
+ // Find radius for the given tangent point on the inner curve such that the
+ // circle is also tangent to the outer curve.
+ void FindPointAndRadius(Point& C, Float& r, const Point& innerTangent,
+ const Point& normal, Float t);
+
+ // Find next dot.
+ Float FindNext(Float overlap);
+
+ // Find mBestOverlap for parameters.
+ void FindBestOverlap(Float aMinR,
+ Float aMinBorderRadius, Float aMaxBorderRadius);
+
+ // Fill corner with dots with given overlap, and return the number of dots
+ // and last two dots's overlap.
+ bool GetCountAndLastOverlap(Float aOverlap,
+ size_t* aCount, Float* aActualOverlap);
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_DottedCornerFinder_h_ */
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -6,16 +6,17 @@
/**
* Code responsible for managing style changes: tracking what style
* changes need to happen, scheduling them, and doing them.
*/
#include "mozilla/RestyleManager.h"
#include <algorithm> // For std::max
+#include "mozilla/EffectSet.h"
#include "mozilla/EventStates.h"
#include "nsLayoutUtils.h"
#include "AnimationCommon.h" // For GetLayerAnimationInfo
#include "FrameLayerBuilder.h"
#include "GeckoProfiler.h"
#include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
#include "nsAutoPtr.h"
#include "nsStyleChangeList.h"
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -10,18 +10,16 @@
#include "mozilla/TouchEvents.h"
#include "mozilla/dom/EventTarget.h"
#include "nsIFrame.h"
#include "nsPresShell.h"
#include "nsView.h"
namespace mozilla {
-using EventTarget = ::mozilla::dom::EventTarget;
-
nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* TouchManager::gCaptureTouchList;
/*static*/ void
TouchManager::InitializeStatics()
{
NS_ASSERTION(!gCaptureTouchList, "InitializeStatics called multiple times!");
gCaptureTouchList = new nsRefPtrHashtable<nsUint32HashKey, dom::Touch>;
}
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -113,19 +113,21 @@ EXPORTS.mozilla += [
'StaticPresData.h',
]
UNIFIED_SOURCES += [
'AccessibleCaret.cpp',
'AccessibleCaretEventHub.cpp',
'AccessibleCaretManager.cpp',
'ActiveLayerTracker.cpp',
+ 'DashedCornerFinder.cpp',
'DisplayItemClip.cpp',
'DisplayItemScrollClip.cpp',
'DisplayListClipState.cpp',
+ 'DottedCornerFinder.cpp',
'FrameLayerBuilder.cpp',
'FramePropertyTable.cpp',
'GeometryUtils.cpp',
'LayoutLogging.cpp',
'MaskLayerImageCache.cpp',
'MobileViewportManager.cpp',
'nsBidi.cpp',
'nsBidiPresUtils.cpp',
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -7,16 +7,18 @@
#include "nsCSSRenderingBorders.h"
#include "gfxUtils.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/PathHelpers.h"
#include "BorderConsts.h"
+#include "DashedCornerFinder.h"
+#include "DottedCornerFinder.h"
#include "nsLayoutUtils.h"
#include "nsStyleConsts.h"
#include "nsCSSColorUtils.h"
#include "GeckoProfiler.h"
#include "nsExpirationTracker.h"
#include "RoundedRect.h"
#include "nsClassHashtable.h"
#include "nsStyleStruct.h"
@@ -56,16 +58,20 @@ static void ComputeBorderCornerDimension
const Rect& aInnerRect,
const RectCornerRadii& aRadii,
RectCornerRadii *aDimsResult);
// given a side index, get the previous and next side index
#define NEXT_SIDE(_s) mozilla::css::Side(((_s) + 1) & 3)
#define PREV_SIDE(_s) mozilla::css::Side(((_s) + 3) & 3)
+// given a corner index, get the previous and next corner index
+#define NEXT_CORNER(_s) mozilla::css::Corner(((_s) + 1) & 3)
+#define PREV_CORNER(_s) mozilla::css::Corner(((_s) + 3) & 3)
+
// from the given base color and the background color, turn
// color into a color for the given border pattern style
static Color MakeBorderColor(nscolor aColor,
nscolor aBackgroundColor,
BorderColorStyle aBorderColorStyle);
// Given a line index (an index starting from the outside of the
@@ -318,18 +324,16 @@ nsCSSBorderRenderer::AreBorderSideFinalS
return true;
}
bool
nsCSSBorderRenderer::IsSolidCornerStyle(uint8_t aStyle, mozilla::css::Corner aCorner)
{
switch (aStyle) {
- case NS_STYLE_BORDER_STYLE_DOTTED:
- case NS_STYLE_BORDER_STYLE_DASHED:
case NS_STYLE_BORDER_STYLE_SOLID:
return true;
case NS_STYLE_BORDER_STYLE_INSET:
case NS_STYLE_BORDER_STYLE_OUTSET:
return (aCorner == NS_CORNER_TOP_LEFT || aCorner == NS_CORNER_BOTTOM_RIGHT);
case NS_STYLE_BORDER_STYLE_GROOVE:
@@ -395,18 +399,16 @@ nsCSSBorderRenderer::IsCornerMergeable(m
}
BorderColorStyle
nsCSSBorderRenderer::BorderColorStyleForSolidCorner(uint8_t aStyle, mozilla::css::Corner aCorner)
{
// note that this function assumes that the corner is already solid,
// as per the earlier function
switch (aStyle) {
- case NS_STYLE_BORDER_STYLE_DOTTED:
- case NS_STYLE_BORDER_STYLE_DASHED:
case NS_STYLE_BORDER_STYLE_SOLID:
case NS_STYLE_BORDER_STYLE_DOUBLE:
return BorderColorStyleSolid;
case NS_STYLE_BORDER_STYLE_INSET:
case NS_STYLE_BORDER_STYLE_GROOVE:
if (aCorner == NS_CORNER_TOP_LEFT)
return BorderColorStyleDark;
@@ -1049,16 +1051,55 @@ nsCSSBorderRenderer::GetStraightBorderPo
P.x += signs[0] * borderWidth / 2.0f;
P.y += signs[1] * dim.height;
}
return P;
}
void
+nsCSSBorderRenderer::GetOuterAndInnerBezier(Bezier* aOuterBezier,
+ Bezier* aInnerBezier,
+ mozilla::css::Corner aCorner)
+{
+ // Return bezier control points for outer and inner curve for given corner.
+ //
+ // ___---+ outer curve
+ // __-- |
+ // _- |
+ // / |
+ // / |
+ // | |
+ // | __--+ inner curve
+ // | _-
+ // | /
+ // | /
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // +---------+
+
+ mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+ mozilla::css::Side sideV(GetVerticalSide(aCorner));
+
+ Size innerCornerSize(std::max(0.0f, mBorderRadii[aCorner].width -
+ mBorderWidths[sideV]),
+ std::max(0.0f, mBorderRadii[aCorner].height -
+ mBorderWidths[sideH]));
+
+ GetBezierPointsForCorner(aOuterBezier, aCorner, mOuterRect.AtCorner(aCorner),
+ mBorderRadii[aCorner]);
+
+ GetBezierPointsForCorner(aInnerBezier, aCorner, mInnerRect.AtCorner(aCorner),
+ innerCornerSize);
+}
+
+void
nsCSSBorderRenderer::FillSolidBorder(const Rect& aOuterRect,
const Rect& aInnerRect,
const RectCornerRadii& aBorderRadii,
const Float* aBorderSizes,
int aSides,
const ColorPattern& aColor)
{
// Note that this function is allowed to draw more than just the
@@ -1287,16 +1328,45 @@ nsCSSBorderRenderer::DrawBorderSides(int
compositeColors = mCompositeColors[i];
break;
}
if (borderRenderStyle == NS_STYLE_BORDER_STYLE_NONE ||
borderRenderStyle == NS_STYLE_BORDER_STYLE_HIDDEN)
return;
+ if (borderRenderStyle == NS_STYLE_BORDER_STYLE_DASHED ||
+ borderRenderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
+ // Draw each corner separately, with the given side's color.
+ if (aSides & SIDE_BIT_TOP) {
+ DrawDashedOrDottedCorner(NS_SIDE_TOP, C_TL);
+ } else if (aSides & SIDE_BIT_LEFT) {
+ DrawDashedOrDottedCorner(NS_SIDE_LEFT, C_TL);
+ }
+
+ if (aSides & SIDE_BIT_TOP) {
+ DrawDashedOrDottedCorner(NS_SIDE_TOP, C_TR);
+ } else if (aSides & SIDE_BIT_RIGHT) {
+ DrawDashedOrDottedCorner(NS_SIDE_RIGHT, C_TR);
+ }
+
+ if (aSides & SIDE_BIT_BOTTOM) {
+ DrawDashedOrDottedCorner(NS_SIDE_BOTTOM, C_BL);
+ } else if (aSides & SIDE_BIT_LEFT) {
+ DrawDashedOrDottedCorner(NS_SIDE_LEFT, C_BL);
+ }
+
+ if (aSides & SIDE_BIT_BOTTOM) {
+ DrawDashedOrDottedCorner(NS_SIDE_BOTTOM, C_BR);
+ } else if (aSides & SIDE_BIT_RIGHT) {
+ DrawDashedOrDottedCorner(NS_SIDE_RIGHT, C_BR);
+ }
+ return;
+ }
+
// -moz-border-colors is a hack; if we have it for a border, then
// it's always drawn solid, and each color is given 1px. The last
// color is used for the remainder of the border's size. Just
// hand off to another function to do all that.
if (compositeColors) {
DrawBorderSidesCompositeColors(aSides, compositeColors);
return;
}
@@ -1311,18 +1381,16 @@ nsCSSBorderRenderer::DrawBorderSides(int
if (mOneUnitBorder &&
(borderRenderStyle == NS_STYLE_BORDER_STYLE_RIDGE ||
borderRenderStyle == NS_STYLE_BORDER_STYLE_GROOVE ||
borderRenderStyle == NS_STYLE_BORDER_STYLE_DOUBLE))
borderRenderStyle = NS_STYLE_BORDER_STYLE_SOLID;
switch (borderRenderStyle) {
case NS_STYLE_BORDER_STYLE_SOLID:
- case NS_STYLE_BORDER_STYLE_DASHED:
- case NS_STYLE_BORDER_STYLE_DOTTED:
borderColorStyleTopLeft[0] = BorderColorStyleSolid;
borderColorStyleBottomRight[0] = BorderColorStyleSolid;
borderColorStyleCount = 1;
break;
case NS_STYLE_BORDER_STYLE_GROOVE:
@@ -1534,17 +1602,17 @@ nsCSSBorderRenderer::DrawBorderSides(int
soRect = siRect;
}
}
void
nsCSSBorderRenderer::SetupDashedOptions(StrokeOptions* aStrokeOptions,
Float aDash[2],
mozilla::css::Side aSide,
- Float aBorderLength)
+ Float aBorderLength, bool isCorner)
{
uint8_t style = mBorderStyles[aSide];
Float borderWidth = mBorderWidths[aSide];
// Dashed line starts and ends with half segment in most case.
//
// __--+---+---+---+---+---+---+---+---+--__
// |###| | |###|###| | |###|
@@ -1682,16 +1750,33 @@ nsCSSBorderRenderer::SetupDashedOptions(
aDash[0] = fullDash;
aDash[1] = fullDash;
if (style == NS_STYLE_BORDER_STYLE_DASHED && fullDash > 1.0f) {
if (!fullStart) {
// Draw half segments on both ends.
aStrokeOptions->mDashOffset = halfDash;
}
+ } else if (isCorner) {
+ // If side ends with filled full segment, corner should start with unfilled
+ // full segment.
+ //
+ // corner side
+ // ------------>|<---------------------------
+ // |
+ // __+---+---+---+---+---+---+---+---+
+ // _+- | |###|###| | |###|###| |
+ // /##| | |###|###| | |###|###| |
+ // +####| | |###|###| | |###|###| |
+ // /#\####| _+--+---+---+---+---+---+---+---+
+ // |####\##+-
+ // |#####+-
+ // +--###/
+ // | --+
+ aStrokeOptions->mDashOffset = fullDash;
}
aStrokeOptions->mDashPattern = aDash;
aStrokeOptions->mDashLength = 2;
PrintAsFormatString("dash: %f %f\n", aDash[0], aDash[1]);
}
@@ -1769,17 +1854,17 @@ nsCSSBorderRenderer::DrawDashedOrDottedS
Float borderLength = GetBorderLength(aSide, start, end);
if (borderLength < 0.0f) {
return;
}
StrokeOptions strokeOptions(borderWidth);
Float dash[2];
- SetupDashedOptions(&strokeOptions, dash, aSide, borderLength);
+ SetupDashedOptions(&strokeOptions, dash, aSide, borderLength, false);
mDrawTarget->StrokeLine(start, end,
ColorPattern(ToDeviceColor(borderColor)),
strokeOptions);
}
void
nsCSSBorderRenderer::DrawDottedSideSlow(mozilla::css::Side aSide)
@@ -2026,16 +2111,251 @@ nsCSSBorderRenderer::DrawDottedSideSlow(
RefPtr<Path> path = builder->Finish();
mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
if (mergeStart == MERGE_HALF || mergeEnd == MERGE_HALF) {
mDrawTarget->PopClip();
}
}
+void
+nsCSSBorderRenderer::DrawDashedOrDottedCorner(mozilla::css::Side aSide,
+ mozilla::css::Corner aCorner)
+{
+ // Draw dashed/dotted corner with following approach.
+ //
+ // dashed corner
+ // If both side has same border-width and border-width <= 2.0, draw dashed
+ // line along the corner, with appropriate dash length and gap to make the
+ // corner symmetric as far as possible. Dash length equals to the gap, and
+ // the ratio of the dash length to border-width is the maximum value in in
+ // [1, 3] range.
+ // Otherwise, draw dashed segments along the corner, keeping same dash
+ // length ratio to border-width at that point.
+ // (see DashedCornerFinder.h for more detail)
+ // Line ends with half segments, to joint with both side easily.
+ //
+ // dotted corner
+ // If both side has same border-width and border-width <= 2.0, draw 1:1
+ // dashed line along the corner.
+ // Otherwise Draw circles along the corner, with appropriate gap that makes
+ // the corner symmetric as far as possible. The size of the circle may
+ // change along the corner, that is tangent to the outer curver and the
+ // inner curve. The ratio of the gap to circle diameter is the maximum
+ // value in [0.5, 1] range.
+ // (see DottedCornerFinder.h for more detail)
+ // Corner ends with filled dots but those dots are drawn by
+ // DrawDashedOrDottedSide. So this may draw no circles if there's no space
+ // between 2 dots at both ends.
+
+ NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED ||
+ mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
+ "Style should be dashed or dotted.");
+
+ if (IsCornerMergeable(aCorner)) {
+ // DrawDashedOrDottedSide will draw corner.
+ return;
+ }
+
+ mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+ mozilla::css::Side sideV(GetVerticalSide(aCorner));
+ Float borderWidthH = mBorderWidths[sideH];
+ Float borderWidthV = mBorderWidths[sideV];
+ if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
+ return;
+ }
+
+ Float styleH = mBorderStyles[sideH];
+ Float styleV = mBorderStyles[sideV];
+
+ // Corner between dotted and others with radius=0 is drawn by side.
+ if (IsZeroSize(mBorderRadii[aCorner]) &&
+ (styleV == NS_STYLE_BORDER_STYLE_DOTTED ||
+ styleH == NS_STYLE_BORDER_STYLE_DOTTED)) {
+ return;
+ }
+
+ if (borderWidthH != borderWidthV || borderWidthH > 2.0f) {
+ uint8_t style = mBorderStyles[aSide];
+ if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
+ DrawDottedCornerSlow(aSide, aCorner);
+ } else {
+ DrawDashedCornerSlow(aSide, aCorner);
+ }
+ return;
+ }
+
+ nscolor borderColor = mBorderColors[aSide];
+ Point points[4];
+ bool ignored;
+ points[0] = GetStraightBorderPoint(sideH, aCorner, &ignored);
+ points[3] = GetStraightBorderPoint(sideV, aCorner, &ignored);
+ // Round points to draw dot on each pixel.
+ if (borderWidthH < 2.0f) {
+ points[0].x = round(points[0].x);
+ }
+ if (borderWidthV < 2.0f) {
+ points[3].y = round(points[3].y);
+ }
+ points[1] = points[0];
+ points[1].x += kKappaFactor * (points[3].x - points[0].x);
+ points[2] = points[3];
+ points[2].y += kKappaFactor * (points[0].y - points[3].y);
+
+ Float len = GetQuarterEllipticArcLength(fabs(points[0].x - points[3].x),
+ fabs(points[0].y - points[3].y));
+
+ Float dash[2];
+ StrokeOptions strokeOptions(borderWidthH);
+ SetupDashedOptions(&strokeOptions, dash, aSide, len, true);
+
+ RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
+ builder->MoveTo(points[0]);
+ builder->BezierTo(points[1], points[2], points[3]);
+ RefPtr<Path> path = builder->Finish();
+ mDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(borderColor)),
+ strokeOptions);
+}
+
+void
+nsCSSBorderRenderer::DrawDottedCornerSlow(mozilla::css::Side aSide,
+ mozilla::css::Corner aCorner)
+{
+ NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
+ "Style should be dotted.");
+
+ mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+ mozilla::css::Side sideV(GetVerticalSide(aCorner));
+ Float R0 = mBorderWidths[sideH] / 2.0f;
+ Float Rn = mBorderWidths[sideV] / 2.0f;
+ if (R0 == 0.0f && Rn == 0.0f) {
+ return;
+ }
+
+ nscolor borderColor = mBorderColors[aSide];
+ Bezier outerBezier;
+ Bezier innerBezier;
+ GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
+
+ bool ignored;
+ Point C0 = GetStraightBorderPoint(sideH, aCorner, &ignored);
+ Point Cn = GetStraightBorderPoint(sideV, aCorner, &ignored);
+ DottedCornerFinder finder(outerBezier, innerBezier, aCorner,
+ mBorderRadii[aCorner].width,
+ mBorderRadii[aCorner].height,
+ C0, R0, Cn, Rn, mBorderCornerDimensions[aCorner]);
+
+ RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
+ while (finder.HasMore()) {
+ DottedCornerFinder::Result result = finder.Next();
+
+ builder->MoveTo(Point(result.C.x + result.r, result.C.y));
+ builder->Arc(result.C, result.r, 0, Float(2.0 * M_PI));
+ }
+ RefPtr<Path> path = builder->Finish();
+ mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
+}
+
+void
+nsCSSBorderRenderer::DrawDashedCornerSlow(mozilla::css::Side aSide,
+ mozilla::css::Corner aCorner)
+{
+ NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED,
+ "Style should be dashed.");
+
+ mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+ mozilla::css::Side sideV(GetVerticalSide(aCorner));
+ Float borderWidthH = mBorderWidths[sideH];
+ Float borderWidthV = mBorderWidths[sideV];
+ if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
+ return;
+ }
+
+ nscolor borderColor = mBorderColors[aSide];
+ Bezier outerBezier;
+ Bezier innerBezier;
+ GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
+
+ DashedCornerFinder finder(outerBezier, innerBezier,
+ borderWidthH, borderWidthV,
+ mBorderCornerDimensions[aCorner]);
+
+ RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
+ while (finder.HasMore()) {
+ DashedCornerFinder::Result result = finder.Next();
+
+ builder->MoveTo(result.outerSectionBezier.mPoints[0]);
+ builder->BezierTo(result.outerSectionBezier.mPoints[1],
+ result.outerSectionBezier.mPoints[2],
+ result.outerSectionBezier.mPoints[3]);
+ builder->LineTo(result.innerSectionBezier.mPoints[3]);
+ builder->BezierTo(result.innerSectionBezier.mPoints[2],
+ result.innerSectionBezier.mPoints[1],
+ result.innerSectionBezier.mPoints[0]);
+ builder->LineTo(result.outerSectionBezier.mPoints[0]);
+ }
+
+ if (outerBezier.mPoints[0].x != innerBezier.mPoints[0].x) {
+ // Fill gap before the first section.
+ //
+ // outnerPoint[0]
+ // |
+ // v
+ // _+-----------+--
+ // / \##########|
+ // / \#########|
+ // + \########|
+ // |\ \######|
+ // | \ \#####|
+ // | \ \####|
+ // | \ \##|
+ // | \ \#|
+ // | \ \|
+ // | \ _-+--
+ // +--------------+ ^
+ // | | |
+ // | | innerPoint[0]
+ // | |
+ builder->MoveTo(outerBezier.mPoints[0]);
+ builder->LineTo(innerBezier.mPoints[0]);
+ builder->LineTo(Point(innerBezier.mPoints[0].x, outerBezier.mPoints[0].y));
+ builder->LineTo(outerBezier.mPoints[0]);
+ }
+
+ if (outerBezier.mPoints[3].y != innerBezier.mPoints[3].y) {
+ // Fill gap after the last section.
+ //
+ // outnerPoint[3]
+ // |
+ // |
+ // | _+-----------+--
+ // | / \ |
+ // v/ \ |
+ // + \ |
+ // |\ \ |
+ // |##\ \ |
+ // |####\ \ |
+ // |######\ \ |
+ // |########\ \ |
+ // |##########\ \|
+ // |############\ _-+--
+ // +--------------+<-- innerPoint[3]
+ // | |
+ // | |
+ // | |
+ builder->MoveTo(outerBezier.mPoints[3]);
+ builder->LineTo(innerBezier.mPoints[3]);
+ builder->LineTo(Point(outerBezier.mPoints[3].x, innerBezier.mPoints[3].y));
+ builder->LineTo(outerBezier.mPoints[3]);
+ }
+
+ RefPtr<Path> path = builder->Finish();
+ mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
+}
+
bool
nsCSSBorderRenderer::AllBordersSameWidth()
{
if (mBorderWidths[0] == mBorderWidths[1] &&
mBorderWidths[0] == mBorderWidths[2] &&
mBorderWidths[0] == mBorderWidths[3])
{
return true;
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -5,16 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NS_CSS_RENDERING_BORDERS_H
#define NS_CSS_RENDERING_BORDERS_H
#include "gfxRect.h"
#include "mozilla/Attributes.h"
#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BezierUtils.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/RefPtr.h"
#include "nsColor.h"
#include "nsCOMPtr.h"
#include "nsStyleConsts.h"
#include "nsPresContext.h"
struct nsBorderColors;
@@ -56,16 +57,17 @@ typedef enum {
BorderColorStyleNone,
BorderColorStyleSolid,
BorderColorStyleLight,
BorderColorStyleDark
} BorderColorStyle;
class nsCSSBorderRenderer final
{
+ typedef mozilla::gfx::Bezier Bezier;
typedef mozilla::gfx::ColorPattern ColorPattern;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::Path Path;
typedef mozilla::gfx::Point Point;
typedef mozilla::gfx::Rect Rect;
typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
typedef mozilla::gfx::StrokeOptions StrokeOptions;
@@ -161,16 +163,22 @@ private:
// is taken care of by the ADD compositing.
already_AddRefed<Path> GetSideClipSubPath(mozilla::css::Side aSide);
// Return start or end point for dashed/dotted side
Point GetStraightBorderPoint(mozilla::css::Side aSide,
mozilla::css::Corner aCorner,
bool* aIsUnfilled);
+ // Return bezier control points for the outer and the inner curve for given
+ // corner
+ void GetOuterAndInnerBezier(Bezier* aOuterBezier,
+ Bezier* aInnerBezier,
+ mozilla::css::Corner aCorner);
+
// Given a set of sides to fill and a color, do so in the fastest way.
//
// Stroke tends to be faster for smaller borders because it doesn't go
// through the tessellator, which has initialization overhead. If
// we're rendering all sides, we can use stroke at any thickness; we
// also do TL/BR pairs at 1px thickness using stroke.
//
// If we can't stroke, then if it's a TL/BR pair, we use the specific
@@ -194,24 +202,36 @@ private:
void DrawBorderSides (int aSides);
// function used by the above to handle -moz-border-colors
void DrawBorderSidesCompositeColors(int aSides, const nsBorderColors *compositeColors);
// Setup the stroke options for the given dashed/dotted side
void SetupDashedOptions(StrokeOptions* aStrokeOptions,
Float aDash[2], mozilla::css::Side aSide,
- Float aBorderLength);
+ Float aBorderLength, bool isCorner);
// Draw the given dashed/dotte side
void DrawDashedOrDottedSide(mozilla::css::Side aSide);
// Draw the given dotted side, each dot separately
void DrawDottedSideSlow(mozilla::css::Side aSide);
+ // Draw the given dashed/dotted corner
+ void DrawDashedOrDottedCorner(mozilla::css::Side aSide,
+ mozilla::css::Corner aCorner);
+
+ // Draw the given dotted corner, each segment separately
+ void DrawDottedCornerSlow(mozilla::css::Side aSide,
+ mozilla::css::Corner aCorner);
+
+ // Draw the given dashed corner, each dot separately
+ void DrawDashedCornerSlow(mozilla::css::Side aSide,
+ mozilla::css::Corner aCorner);
+
// Analyze if all border sides have the same width.
bool AllBordersSameWidth();
// Analyze if all borders are 'solid' this also considers hidden or 'none'
// borders because they can be considered 'solid' borders of 0 width and
// with no color effect.
bool AllBordersSolid(bool *aHasCompositeColors);